Browse Source

Right sidebar changes:

Search and forward are tabs now
Fix history navigation
master
morethanwords 4 years ago
parent
commit
d4e93c819e
  1. 4
      src/components/chat/contextMenu.ts
  2. 50
      src/components/horizontalMenu.ts
  3. 56
      src/components/sidebarRight/forward.ts
  4. 50
      src/components/sidebarRight/search.ts
  5. 793
      src/components/sidebarRight/sharedMedia.ts
  6. 20
      src/components/slider.ts
  7. 12
      src/index.hbs
  8. 49
      src/lib/appManagers/appImManager.ts
  9. 12
      src/lib/appManagers/appMediaViewer.ts
  10. 2
      src/lib/appManagers/appSidebarLeft.ts
  11. 836
      src/lib/appManagers/appSidebarRight.ts
  12. 5
      src/lib/utils.ts
  13. 4
      src/scss/partials/_rightSidebar.scss

4
src/components/chat/contextMenu.ts

@ -2,8 +2,8 @@ import appChatsManager from "../../lib/appManagers/appChatsManager"; @@ -2,8 +2,8 @@ import appChatsManager from "../../lib/appManagers/appChatsManager";
import appImManager from "../../lib/appManagers/appImManager";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import appPeersManager from "../../lib/appManagers/appPeersManager";
import appSidebarRight from "../../lib/appManagers/appSidebarRight";
import { findUpClassName, $rootScope } from "../../lib/utils";
import appForward from "../appForward";
import { parseMenuButtonsTo, attachContextMenuListener, positionMenu, openBtnMenu } from "../misc";
import { PopupButton, PopupPeer } from "../popup";
@ -149,7 +149,7 @@ export class ChatContextMenu { @@ -149,7 +149,7 @@ export class ChatContextMenu {
});
this.buttons.forward.addEventListener('click', () => {
appForward.init([this.msgID]);
appSidebarRight.forwardTab.open([this.msgID]);
});
this.buttons.edit.addEventListener('click', () => {

50
src/components/horizontalMenu.ts

@ -1,25 +1,11 @@ @@ -1,25 +1,11 @@
import { findUpTag, whichChild } from "../lib/utils";
function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
/* if(toRight) {
//prevTabContent.style.filter = `brightness(80%)`;
prevTabContent.style.transform = `translateX(-25%)`;
tabContent.style.transform = `translateX(20%)`;
} else {
//tabContent.style.filter = `brightness(80%)`;
tabContent.style.transform = `translateX(-25%)`;
prevTabContent.style.transform = `translateX(20%)`;
} */
const width = prevTabContent.getBoundingClientRect().width;
if(toRight) {
prevTabContent.style.filter = `brightness(80%)`;
prevTabContent.style.transform = `translate3d(${-width * .25}px, 0, 0)`;
tabContent.style.transform = `translate3d(${width}px, 0, 0)`;
} else {
tabContent.style.filter = `brightness(80%)`;
tabContent.style.transform = `translate3d(${-width * .25}px, 0, 0)`;
prevTabContent.style.transform = `translate3d(${width}px, 0, 0)`;
}
function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, width: number, toRight: boolean) {
const elements = [tabContent, prevTabContent];
if(toRight) elements.reverse();
elements[0].style.filter = `brightness(80%)`;
elements[0].style.transform = `translate3d(${-width * .25}px, 0, 0)`;
elements[1].style.transform = `translate3d(${width}px, 0, 0)`;
tabContent.classList.add('active');
void tabContent.offsetWidth; // reflow
@ -28,15 +14,11 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t @@ -28,15 +14,11 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t
tabContent.style.filter = '';
}
function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
const width = prevTabContent.getBoundingClientRect().width;
if(toRight) {
tabContent.style.transform = `translate3d(${width}px, 0, 0)`;
prevTabContent.style.transform = `translate3d(${-width}px, 0, 0)`;
} else {
tabContent.style.transform = `translate3d(${-width}px, 0, 0)`;
prevTabContent.style.transform = `translate3d(${width}px, 0, 0)`;
}
function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, width: number, toRight: boolean) {
const elements = [tabContent, prevTabContent];
if(toRight) elements.reverse();
elements[0].style.transform = `translate3d(${-width}px, 0, 0)`;
elements[1].style.transform = `translate3d(${width}px, 0, 0)`;
tabContent.classList.add('active');
void tabContent.offsetWidth; // reflow
@ -72,11 +54,15 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick? @@ -72,11 +54,15 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
}
const toRight = prevId < id;
if(prevId != -1) {
if(!tabContent) {
//prevTabContent.classList.remove('active');
} else if(prevId != -1) {
const width = prevTabContent.getBoundingClientRect().width;
if(tabs || content.dataset.slider == 'tabs') {
slideTabs(tabContent, prevTabContent, toRight);
slideTabs(tabContent, prevTabContent, width, toRight);
} else {
slideNavigation(tabContent, prevTabContent, toRight);
slideNavigation(tabContent, prevTabContent, width, toRight);
}
} else {
tabContent.classList.add('active');

56
src/components/appForward.ts → src/components/sidebarRight/forward.ts

@ -1,20 +1,32 @@ @@ -1,20 +1,32 @@
import appSidebarRight from "../lib/appManagers/appSidebarRight";
import appMessagesManager from "../lib/appManagers/appMessagesManager";
import { putPreloader } from "./misc";
import { AppSelectPeers } from "./appSelectPeers";
import appSidebarRight, { AppSidebarRight } from "../../lib/appManagers/appSidebarRight";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import { putPreloader } from "../misc";
import { AppSelectPeers } from "../appSelectPeers";
import { SliderTab } from "../slider";
class AppForward {
public container = document.getElementById('forward-container') as HTMLDivElement;
private closeBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement;
private sendBtn = this.container.querySelector('.btn-circle') as HTMLButtonElement;
export default class AppForwardTab implements SliderTab {
public container: HTMLElement;
public closeBtn: HTMLElement;
private sendBtn: HTMLButtonElement;
private selector: AppSelectPeers;
private msgIDs: number[] = [];
private sidebarWasActive: boolean;
onCloseAfterTimeout() {
this.cleanup();
}
public cleanup() {
if(this.selector) {
this.selector.container.remove();
this.selector = null;
}
}
constructor() {
this.closeBtn.addEventListener('click', this.close.bind(this));
public init() {
this.container = document.getElementById('forward-container') as HTMLDivElement;
this.closeBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement;
this.sendBtn = this.container.querySelector('.btn-circle') as HTMLButtonElement;
this.sendBtn.addEventListener('click', () => {
let peerIDs = this.selector.getSelected();
@ -44,25 +56,15 @@ class AppForward { @@ -44,25 +56,15 @@ class AppForward {
});
}
public close() {
(this.sidebarWasActive ? Promise.resolve() : appSidebarRight.toggleSidebar(false)).then(() => {
this.cleanup();
this.container.classList.remove('active');
});
}
public cleanup() {
if(this.selector) {
this.selector.container.remove();
this.selector = null;
public open(ids: number[]) {
if(this.init) {
this.init();
this.init = null;
}
}
public init(ids: number[]) {
this.cleanup();
this.msgIDs = ids;
this.container.classList.add('active');
this.sendBtn.innerHTML = '';
this.sendBtn.classList.add('tgico-send');
this.sendBtn.disabled = false;
@ -75,10 +77,8 @@ class AppForward { @@ -75,10 +77,8 @@ class AppForward {
}
}, ['dialogs', 'contacts'], () => {
//console.log('forward rendered:', this.container.querySelector('.selector ul').childElementCount);
this.sidebarWasActive = appSidebarRight.sidebarEl.classList.contains('active');
appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.forward);
appSidebarRight.toggleSidebar(true);
});
}
}
export default new AppForward();

50
src/components/sidebarRight/search.ts

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
import appSidebarRight, { AppSidebarRight } from "../../lib/appManagers/appSidebarRight";
import AppSearch, { SearchGroup } from "../appSearch";
import SearchInput from "../searchInput";
import { SliderTab } from "../slider";
export default class AppPrivateSearchTab implements SliderTab {
public container: HTMLElement;
public closeBtn: HTMLElement;
private searchInput: SearchInput;
private appSearch: AppSearch;
private peerID = 0;
onOpenAfterTimeout() {
this.appSearch.beginSearch(this.peerID);
}
onCloseAfterTimeout() {
this.peerID = 0;
this.appSearch.reset();
}
public init() {
this.container = document.getElementById('search-private-container');
this.closeBtn = this.container.querySelector('.sidebar-close-button');
this.searchInput = new SearchInput('Search');
this.closeBtn.parentElement.append(this.searchInput.container);
this.appSearch = new AppSearch(this.container.querySelector('.chats-container'), this.searchInput, {
messages: new SearchGroup('Private Search', 'messages')
});
}
open(peerID: number) {
if(this.init) {
this.init();
this.init = null;
}
if(this.peerID != 0) {
this.appSearch.beginSearch(this.peerID);
return;
}
this.peerID = peerID;
appSidebarRight.selectTab(AppSidebarRight.SLIDERITEMSIDS.search);
appSidebarRight.toggleSidebar(true);
}
}

793
src/components/sidebarRight/sharedMedia.ts

@ -0,0 +1,793 @@ @@ -0,0 +1,793 @@
import appImManager from "../../lib/appManagers/appImManager";
import appMediaViewer from "../../lib/appManagers/appMediaViewer";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import appPeersManager from "../../lib/appManagers/appPeersManager";
import appPhotosManager from "../../lib/appManagers/appPhotosManager";
import appProfileManager from "../../lib/appManagers/appProfileManager";
import appUsersManager from "../../lib/appManagers/appUsersManager";
import { logger, LogLevels } from "../../lib/logger";
import { RichTextProcessor } from "../../lib/richtextprocessor";
import { $rootScope } from "../../lib/utils";
import AvatarElement from "../avatar";
import { horizontalMenu } from "../horizontalMenu";
import LazyLoadQueue from "../lazyLoadQueue";
import { renderImageFromUrl, putPreloader } from "../misc";
import Scrollable from "../scrollable_new";
import { SliderTab } from "../slider";
import { wrapDocument, wrapAudio } from "../wrappers";
const testScroll = false;
let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => {
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
el.style.display = '';
});
};
type ContentType = 'contentMembers' | 'contentMedia' | 'contentDocuments' | 'contentLinks' | 'contentAudio';
type SharedMediaType = 'inputMessagesFilterContacts' | 'inputMessagesFilterPhotoVideo' | 'inputMessagesFilterDocument' | 'inputMessagesFilterUrl' | 'inputMessagesFilterMusic';
const contentToSharedMap: {[contentType in ContentType]: SharedMediaType} = {
contentMembers: 'inputMessagesFilterContacts',
contentMedia: 'inputMessagesFilterPhotoVideo',
contentDocuments: 'inputMessagesFilterDocument',
contentLinks: 'inputMessagesFilterUrl',
contentAudio: 'inputMessagesFilterMusic'
};
// TODO: отправленное сообщение с картинкой, или же новое полученное апдейтом сообщение не отобразится в медии
// TODO: по-хорошему, нужно просто сделать апдейты для всего сайдбара
export default class AppSharedMediaTab implements SliderTab {
public container: HTMLElement;
public closeBtn: HTMLElement;
private peerID = 0;
public profileContentEl: HTMLDivElement;
public contentContainer: HTMLDivElement;
public profileElements: {
avatar: AvatarElement,
name: HTMLDivElement,
subtitle: HTMLDivElement,
bio: HTMLDivElement,
username: HTMLDivElement,
phone: HTMLDivElement,
notificationsRow: HTMLDivElement,
notificationsCheckbox: HTMLInputElement,
notificationsStatus: HTMLParagraphElement
} = {} as any;
public sharedMedia: {
[t in ContentType]: HTMLDivElement
} = {} as any;
private loadSidebarMediaPromises: {[type: string]: Promise<void>} = {};
private loadedAllMedia: {[type: string]: boolean} = {};
public sharedMediaTypes: SharedMediaType[] = [
//'members',
'inputMessagesFilterContacts',
'inputMessagesFilterPhotoVideo',
'inputMessagesFilterDocument',
'inputMessagesFilterUrl',
'inputMessagesFilterMusic'
];
public sharedMediaType: SharedMediaType = 'inputMessagesFilterPhotoVideo';
private sharedMediaSelected: HTMLDivElement = null;
private lazyLoadQueue = new LazyLoadQueue();
public historiesStorage: {
[peerID: number]: Partial<{
[type in SharedMediaType]: number[]
}>
} = {};
public usedFromHistory: Partial<{
[type in SharedMediaType]: number
}> = {};
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);
public init() {
this.container = document.getElementById('shared-media-container');
this.closeBtn = this.container.querySelector('.sidebar-close-button');
this.profileContentEl = this.container.querySelector('.profile-content');
this.contentContainer = this.container.querySelector('.content-container');
this.profileElements = {
avatar: this.profileContentEl.querySelector('.profile-avatar'),
name: this.profileContentEl.querySelector('.profile-name'),
subtitle: this.profileContentEl.querySelector('.profile-subtitle'),
bio: this.profileContentEl.querySelector('.profile-row-bio'),
username: this.profileContentEl.querySelector('.profile-row-username'),
phone: this.profileContentEl.querySelector('.profile-row-phone'),
notificationsRow: this.profileContentEl.querySelector('.profile-row-notifications'),
notificationsCheckbox: this.profileContentEl.querySelector('#profile-notifications'),
notificationsStatus: this.profileContentEl.querySelector('.profile-row-notifications > p')
};
this.sharedMedia = {
contentMembers: this.profileContentEl.querySelector('#content-members'),
contentMedia: this.profileContentEl.querySelector('#content-media'),
contentDocuments: this.profileContentEl.querySelector('#content-docs'),
contentLinks: this.profileContentEl.querySelector('#content-links'),
contentAudio: this.profileContentEl.querySelector('#content-audio'),
};
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', undefined, 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.contentMedia.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.getMessage(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};
});
appMediaViewer.openMedia(message, target, false, this.container, targets.slice(idx + 1).reverse(), targets.slice(0, idx).reverse(), true);
});
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
//let checked = this.profileElements.notificationsCheckbox.checked;
appImManager.mutePeer(this.peerID);
});
/* this.closeBtn.addEventListener('click', () => {
this.toggleSidebar(false);
}); */
}
private onTransitionStart = () => {
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
const container = this.scroll.container;
if(container.style.overflowY !== 'hidden') {
const scrollBarWidth = container.offsetWidth - container.clientWidth;
container.style.overflowY = 'hidden';
container.style.paddingRight = `${scrollBarWidth}px`;
}
};
private onTransitionEnd = () => {
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
const container = this.scroll.container;
container.style.overflowY = '';
container.style.paddingRight = '0';
};
public filterMessagesByType(ids: number[], type: string) {
let messages: any[] = [];
for(let mid of ids) {
let message = appMessagesManager.getMessage(mid);
if(message.media) messages.push(message);
}
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 || message.media.document.type == 'voice' || message.media.document.type == 'audio') {
continue;
}
let doc = message.media.document;
if(doc.attributes) {
if(doc.attributes.find((a: any) => a._ == "documentAttributeSticker")) {
continue;
}
}
filtered.push(message);
}
break;
}
case 'inputMessagesFilterUrl': {
for(let message of messages) {
if(!message.media.webpage || message.media.webpage._ == 'webPageEmpty') {
continue;
}
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) {
const peerID = this.peerID;
const elemsToAppend: HTMLElement[] = [];
const promises: Promise<any>[] = [];
let sharedMediaDiv: HTMLDivElement;
/* 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': {
sharedMediaDiv = this.sharedMedia.contentMedia;
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('media-item');
//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($rootScope.selectedPeerID != peerID) {
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('media-image', '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('media-image');
if(needBlur) img.style.opacity = '0';
div.append(img);
if(isDownloaded || willHaveThumb) {
const promise = new Promise((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 'inputMessagesFilterDocument': {
sharedMediaDiv = this.sharedMedia.contentDocuments;
for(let message of messages) {
let div = wrapDocument(message.media.document, true, false, message.mid);
elemsToAppend.push(div);
}
break;
}
case 'inputMessagesFilterUrl': {
sharedMediaDiv = this.sharedMedia.contentLinks;
for(let message of messages) {
let webpage = message.media.webpage;
let div = document.createElement('div');
let previewDiv = document.createElement('div');
previewDiv.classList.add('preview');
//this.log('wrapping webpage', webpage);
previewDiv.innerText = (webpage.title || webpage.description || webpage.url || webpage.display_url).slice(0, 1);
previewDiv.classList.add('empty');
if(webpage.photo) {
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 60, 60))
.then(() => {
if($rootScope.selectedPeerID != peerID) {
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 = webpage.display_url.split('/', 1)[0];
}
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;
}
case 'inputMessagesFilterMusic': {
sharedMediaDiv = this.sharedMedia.contentAudio;
for(let message of messages) {
let div = wrapAudio(message.media.document, true, message.mid);
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.log.warn('peer changed');
return;
}
}
if(elemsToAppend.length) {
sharedMediaDiv.append(...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) {
if(testScroll/* || 1 == 1 */) {
return;
}
this.log('loadSidebarMedia', single, this.peerID, this.loadSidebarMediaPromises);
const peerID = this.peerID;
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes;
typesToLoad = typesToLoad.filter(type => !this.loadedAllMedia[type]);
if(!typesToLoad.length) return;
const loadCount = (appPhotosManager.windowH / 130 | 0) * 3; // 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] = []);
// render from cache
if(history.length && this.usedFromHistory[type] < history.length) {
let messages: any[] = [];
let used = this.usedFromHistory[type];
do {
let ids = history.slice(used, used + loadCount);
this.log('loadSidebarMedia: will render from cache', used, history, ids, loadCount);
used += ids.length;
messages.push(...this.filterMessagesByType(ids, type));
} while(messages.length < loadCount && used < history.length);
// если перебор
if(messages.length > 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;
let ids = !maxID && appMessagesManager.historiesStorage[peerID]
? appMessagesManager.historiesStorage[peerID].history.slice() : [];
maxID = !maxID && ids.length ? ids[ids.length - 1] : maxID;
this.log('loadSidebarMedia: search house of glass pre', type, ids, maxID);
//let loadCount = history.length ? 50 : 15;
return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, loadCount)
.then(value => {
ids = ids.concat(value.history);
history.push(...ids);
this.log('loadSidebarMedia: search house of glass', type, value, ids);
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
return;
}
if(value.history.length < loadCount) {
this.loadedAllMedia[type] = true;
}
this.usedFromHistory[type] = history.length;
//if(ids.length) {
return this.performSearchResult(this.filterMessagesByType(ids, type), type);
//}
}, (err) => {
this.log.error('load error:', err);
}).then(() => {
this.loadSidebarMediaPromises[type] = null;
});
});
return Promise.all(promises);
}
public cleanup() {
this.loadSidebarMediaPromises = {};
this.loadedAllMedia = {};
this.prevTabID = -1;
this.mediaDivsByIDs = {};
this.lazyLoadQueue.clear();
this.sharedMediaTypes.forEach(type => {
this.usedFromHistory[type] = 0;
});
this.sharedMediaType = 'inputMessagesFilterPhotoVideo';
}
public cleanupHTML() {
//this.contentContainer.classList.remove('loaded');
//this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
if(this.urlsToRevoke.length) {
this.urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
});
this.urlsToRevoke.length = 0;
}
(Object.keys(this.sharedMedia) as ContentType[]).forEach(key => {
this.sharedMedia[key].innerHTML = '';
const inputFilter = contentToSharedMap[key];
if(!this.historiesStorage[this.peerID] || !this.historiesStorage[this.peerID][inputFilter]) {
const parent = this.sharedMedia[key].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('media-item');
div.dataset.id = '' + (i / 3 | 0);
//div.innerText = '' + (i / 3 | 0);
this.sharedMedia.contentMedia.append(div);
}
}
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media
}
public setLoadMutex(promise: Promise<any>) {
this.loadMutex = promise;
}
public setPeer(peerID: number) {
if(this.init) {
this.init();
this.init = null;
}
this.peerID = peerID;
this.cleanup();
}
public fillProfileElements() {
let peerID = this.peerID = $rootScope.selectedPeerID;
this.cleanupHTML();
this.profileElements.avatar.setAttribute('peer', '' + peerID);
// username
if(peerID != $rootScope.myID) {
let username = appPeersManager.getPeerUsername(peerID);
if(username) {
setText(appPeersManager.getPeerUsername(peerID), this.profileElements.username);
}
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog) {
let muted = false;
if(dialog.notify_settings && dialog.notify_settings.mute_until) {
muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date();
}
appImManager.setMutedState(muted);
}
} else {
window.requestAnimationFrame(() => {
this.profileElements.notificationsRow.style.display = 'none';
});
}
//let membersLi = this.profileTabs.firstElementChild.children[0] as HTMLLIElement;
if(peerID > 0) {
//membersLi.style.display = 'none';
let user = appUsersManager.getUser(peerID);
if(user.phone && peerID != $rootScope.myID) {
setText(user.rPhone, this.profileElements.phone);
}
appProfileManager.getProfile(peerID, true).then(userFull => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
if(userFull.rAbout && peerID != $rootScope.myID) {
setText(userFull.rAbout, this.profileElements.bio);
}
//this.log('userFull', userFull);
});
} else {
//membersLi.style.display = appPeersManager.isBroadcast(peerID) ? 'none' : '';
let chat = appPeersManager.getPeer(peerID);
appProfileManager.getChatFull(chat.id).then((chatFull: any) => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
//this.log('chatInfo res 2:', chatFull);
if(chatFull.about) {
setText(RichTextProcessor.wrapRichText(chatFull.about), this.profileElements.bio);
}
});
}
}
/* onOpen() {
this.scroll.onScroll();
} */
onOpenAfterTimeout() {
this.scroll.onScroll();
}
}

20
src/components/slider.ts

@ -7,21 +7,23 @@ export interface SliderTab { @@ -7,21 +7,23 @@ export interface SliderTab {
onCloseAfterTimeout?: () => void
}
const TRANSITIONTIME = 250;
const TRANSITION_TIME = 250;
export default class SidebarSlider {
protected _selectTab: (id: number) => void;
public historyTabIDs: number[] = [];
constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab}) {
this._selectTab = horizontalMenu(null, this.sidebarEl.querySelector('.sidebar-slider') as HTMLDivElement, null, null, TRANSITIONTIME);
this._selectTab(0);
constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab}, canHideFirst = false) {
this._selectTab = horizontalMenu(null, this.sidebarEl.querySelector('.sidebar-slider') as HTMLDivElement, null, null, TRANSITION_TIME);
if(!canHideFirst) {
this._selectTab(0);
}
let onCloseBtnClick = () => {
//console.log('sidebar-close-button click:', this.historyTabIDs);
let closingID = this.historyTabIDs.pop(); // pop current
this.onCloseTab(closingID);
this._selectTab(this.historyTabIDs[this.historyTabIDs.length - 1] || 0);
this._selectTab(this.historyTabIDs[this.historyTabIDs.length - 1] ?? (canHideFirst ? -1 : 0));
};
Array.from(this.sidebarEl.querySelectorAll('.sidebar-close-button') as any as HTMLElement[]).forEach(el => {
el.addEventListener('click', onCloseBtnClick);
@ -30,7 +32,7 @@ export default class SidebarSlider { @@ -30,7 +32,7 @@ export default class SidebarSlider {
public selectTab(id: number) {
if(this.historyTabIDs[this.historyTabIDs.length - 1] == id) {
return;
return false;
}
const tab = this.tabs[id];
@ -42,13 +44,13 @@ export default class SidebarSlider { @@ -42,13 +44,13 @@ export default class SidebarSlider {
if(tab.onOpenAfterTimeout) {
setTimeout(() => {
tab.onOpenAfterTimeout();
}, TRANSITIONTIME);
}, TRANSITION_TIME);
}
}
this.historyTabIDs.push(id);
this._selectTab(id);
return true;
}
public removeTabFromHistory(id: number) {
@ -66,7 +68,7 @@ export default class SidebarSlider { @@ -66,7 +68,7 @@ export default class SidebarSlider {
if(tab.onCloseAfterTimeout) {
setTimeout(() => {
tab.onCloseAfterTimeout();
}, TRANSITIONTIME);
}, TRANSITION_TIME);
}
}
}

12
src/index.hbs

@ -588,14 +588,14 @@ @@ -588,14 +588,14 @@
</div>
<div class="sidebar sidebar-right main-column" id="column-right">
<div class="sidebar-content sidebar-slider tabs-container">
<div class="sidebar-slider-item profile-container">
<div class="sidebar-slider-item profile-container" id="shared-media-container">
<div class="sidebar-header">
<button class="btn-icon tgico sidebar-close-button"></button>
<div class="sidebar-header__title">Info</div>
<!-- <button class="btn-icon rp tgico-edit sidebar-edit-button"></button> -->
<div class="btn-icon tgico-more rp"></div>
<div class="btn-icon tgico-more"></div>
<!-- <div class="btn-icon tgico-more rp btn-menu-toggle">
<div class="btn-menu bottom-left"> -->
<!-- <div class="btn-menu-item menu-mute rp">Mute</div>
@ -649,20 +649,20 @@ @@ -649,20 +649,20 @@
</div>
</div>
</div>
<div class="sidebar-slider-item sidebar-search chats-container" id="search-private-container">
<div class="sidebar-slider-item chats-container" id="search-private-container">
<div class="sidebar-header">
<button class="btn-icon tgico-close sidebar-close-button"></button>
</div>
<div class="chats-container"></div>
</div>
<div class="sidebar-slider-item sidebar-search" id="forward-container">
<div class="sidebar-slider-item" id="forward-container">
<div class="sidebar-header">
<button class="btn-icon tgico-close sidebar-close-button"></button>
<div class="sidebar-header__title">Forward</div>
</div>
<button class="btn-primary btn-circle btn-icon rp btn-corner tgico-send"></button>
</div>
<div class="sidebar-slider-item sidebar-search chats-container" id="stickers-container">
<div class="sidebar-slider-item chats-container" id="stickers-container">
<div class="sidebar-header">
<button class="btn-icon tgico-close sidebar-close-button"></button>
</div>
@ -675,7 +675,7 @@ @@ -675,7 +675,7 @@
</div>
<div class="sidebar-content"><div class="poll-results"></div></div>
</div>
<div class="sidebar-slider-item sidebar-search chats-container" id="search-gifs-container">
<div class="sidebar-slider-item chats-container" id="search-gifs-container">
<div class="sidebar-header">
<button class="btn-icon tgico-close sidebar-close-button"></button>
</div>

49
src/lib/appManagers/appImManager.ts

@ -8,7 +8,7 @@ import appProfileManager from "./appProfileManager"; @@ -8,7 +8,7 @@ import appProfileManager from "./appProfileManager";
import appDialogsManager from "./appDialogsManager";
import { RichTextProcessor } from "../richtextprocessor";
import appPhotosManager from "./appPhotosManager";
import appSidebarRight from './appSidebarRight';
import appSidebarRight, { AppSidebarRight } from './appSidebarRight';
import { logger, LogLevels } from "../logger";
import appMediaViewer from "./appMediaViewer";
@ -23,7 +23,6 @@ import Scrollable from '../../components/scrollable_new'; @@ -23,7 +23,6 @@ import Scrollable from '../../components/scrollable_new';
import BubbleGroups from '../../components/bubbleGroups';
import LazyLoadQueue from '../../components/lazyLoadQueue';
import appDocsManager from './appDocsManager';
import appForward from '../../components/appForward';
import appStickersManager from './appStickersManager';
import AvatarElement from '../../components/avatar';
import appInlineBotsManager from './AppInlineBotsManager';
@ -511,7 +510,7 @@ export class AppImManager { @@ -511,7 +510,7 @@ export class AppImManager {
return;
} else if(target.classList.contains('forward')) {
const mid = +bubble.dataset.mid;
appForward.init([mid]);
appSidebarRight.forwardTab.open([mid]);
return;
} else if(target.classList.contains('name')) {
let peerID = +target.dataset.peerID;
@ -560,7 +559,7 @@ export class AppImManager { @@ -560,7 +559,7 @@ export class AppImManager {
this.searchBtn.addEventListener('click', (e) => {
cancelEvent(e);
if(this.peerID) {
appSidebarRight.beginSearch();
appSidebarRight.searchTab.open(this.peerID);
}
});
@ -610,8 +609,8 @@ export class AppImManager { @@ -610,8 +609,8 @@ export class AppImManager {
if(e.key == 'Escape') {
if(appMediaViewer.wholeDiv.classList.contains('active')) {
appMediaViewer.buttons.close.click();
} else if(appForward.container.classList.contains('active')) {
appForward.close();
} else if(appSidebarRight.historyTabIDs.slice(-1)[0] == AppSidebarRight.SLIDERITEMSIDS.forward) {
appSidebarRight.forwardTab.closeBtn.click();
} else if(this.chatInputC.replyElements.container.classList.contains('active')) {
this.chatInputC.replyElements.cancelBtn.click();
} else if(this.peerID != 0) { // hide current dialog
@ -885,16 +884,18 @@ export class AppImManager { @@ -885,16 +884,18 @@ export class AppImManager {
public setPeerStatus(needClear = false) {
if(!this.myID) return;
const profileElements = appSidebarRight.sharedMediaTab.profileElements;
if(this.peerID < 0) { // not human
let chat = appPeersManager.getPeer(this.peerID);
let isChannel = appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID);
this.subtitleEl.classList.remove('online');
appSidebarRight.profileElements.subtitle.classList.remove('online');
profileElements.subtitle.classList.remove('online');
///////this.log('setPeerStatus', chat);
if(needClear) {
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = '';
this.subtitleEl.innerText = profileElements.subtitle.innerText = '';
}
appProfileManager.getChatFull(chat.id).then((chatInfo: any) => {
@ -904,7 +905,7 @@ export class AppImManager { @@ -904,7 +905,7 @@ export class AppImManager {
if(participants_count) {
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'followers' : 'members');
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = subtitle;
this.subtitleEl.innerText = profileElements.subtitle.innerText = subtitle;
if(participants_count < 2) return;
appChatsManager.getOnlines(chat.id).then(onlines => {
@ -912,7 +913,7 @@ export class AppImManager { @@ -912,7 +913,7 @@ export class AppImManager {
subtitle += ', ' + numberWithCommas(onlines) + ' online';
}
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = subtitle;
this.subtitleEl.innerText = profileElements.subtitle.innerText = subtitle;
});
}
});
@ -920,16 +921,16 @@ export class AppImManager { @@ -920,16 +921,16 @@ export class AppImManager {
let user = appUsersManager.getUser(this.peerID);
if(this.myID == this.peerID) {
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = '';
this.subtitleEl.innerText = profileElements.subtitle.innerText = '';
} else if(user && user.status) {
let subtitle = appUsersManager.getUserStatusString(user.id);
if(subtitle == 'online') {
this.subtitleEl.classList.add('online');
appSidebarRight.profileElements.subtitle.classList.add('online');
profileElements.subtitle.classList.add('online');
}
appSidebarRight.profileElements.subtitle.innerText = subtitle;
profileElements.subtitle.innerText = subtitle;
if(this.typingUsers[this.peerID] == this.peerID) {
this.subtitleEl.innerText = 'typing...';
@ -939,13 +940,13 @@ export class AppImManager { @@ -939,13 +940,13 @@ export class AppImManager {
if(subtitle != 'online') {
this.subtitleEl.classList.remove('online');
appSidebarRight.profileElements.subtitle.classList.remove('online');
profileElements.subtitle.classList.remove('online');
}
}
}
} else {
this.subtitleEl.innerText = 'bot';
appSidebarRight.profileElements.subtitle.innerText = 'bot';
profileElements.subtitle.innerText = 'bot';
}
}
@ -1050,8 +1051,8 @@ export class AppImManager { @@ -1050,8 +1051,8 @@ export class AppImManager {
return true;
}
} else {
appSidebarRight.searchCloseBtn.click();
} else if(appSidebarRight.historyTabIDs[appSidebarRight.historyTabIDs.length - 1] == AppSidebarRight.SLIDERITEMSIDS.search) {
appSidebarRight.searchTab.closeBtn?.click();
}
// set new
@ -1088,7 +1089,7 @@ export class AppImManager { @@ -1088,7 +1089,7 @@ export class AppImManager {
const {promise, cached} = this.getHistory(lastMsgID, true, isJump, additionMsgID);
if(!samePeer) {
appSidebarRight.setPeer(this.peerID);
appSidebarRight.sharedMediaTab.setPeer(this.peerID);
} else {
this.peerChanged = true;
}
@ -1207,8 +1208,8 @@ export class AppImManager { @@ -1207,8 +1208,8 @@ export class AppImManager {
//appSidebarRight.setLoadMutex(this.setPeerPromise);
//}
appSidebarRight.setLoadMutex(this.setPeerPromise);
appSidebarRight.loadSidebarMedia(true);
appSidebarRight.sharedMediaTab.setLoadMutex(this.setPeerPromise);
appSidebarRight.sharedMediaTab.loadSidebarMedia(true);
return this.setPeerPromise;
}
@ -1252,14 +1253,14 @@ export class AppImManager { @@ -1252,14 +1253,14 @@ export class AppImManager {
let title = '';
if(this.peerID == this.myID) title = 'Saved Messages';
else title = appPeersManager.getPeerTitle(this.peerID);
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title;
this.titleEl.innerHTML = appSidebarRight.sharedMediaTab.profileElements.name.innerHTML = title;
this.goDownBtn.classList.remove('hide');
this.setPeerStatus(true);
});
appSidebarRight.fillProfileElements();
appSidebarRight.sharedMediaTab.fillProfileElements();
$rootScope.$broadcast('peer_changed', this.peerID);
}
@ -2536,8 +2537,8 @@ export class AppImManager { @@ -2536,8 +2537,8 @@ export class AppImManager {
}
public setMutedState(muted = false) {
appSidebarRight.profileElements.notificationsCheckbox.checked = !muted;
appSidebarRight.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
appSidebarRight.sharedMediaTab.profileElements.notificationsCheckbox.checked = !muted;
appSidebarRight.sharedMediaTab.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
if(appPeersManager.isBroadcast(this.peerID)) { // not human
this.btnMute.classList.remove('tgico-mute', 'tgico-unmute');

12
src/lib/appManagers/appMediaViewer.ts

@ -10,12 +10,12 @@ import VideoPlayer from "../mediaPlayer"; @@ -10,12 +10,12 @@ import VideoPlayer from "../mediaPlayer";
import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc";
import AvatarElement from "../../components/avatar";
import { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
import appForward from "../../components/appForward";
import { touchSupport } from "../config";
import appMediaPlaybackController from "../../components/appMediaPlaybackController";
import { deferredPromise } from "../../helpers/cancellablePromise";
import mediaSizes from "../../helpers/mediaSizes";
import { isSafari } from "../../helpers/userAgent";
import appSidebarRight, { AppSidebarRight } from "./appSidebarRight";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -155,9 +155,9 @@ export class AppMediaViewer { @@ -155,9 +155,9 @@ export class AppMediaViewer {
this.loadMediaPromiseUp = this.loadMediaPromiseDown = null;
this.setMoverPromise = null;
if(appForward.container.classList.contains('active')) {
if(appSidebarRight.historyTabIDs.slice(-1)[0] == AppSidebarRight.SLIDERITEMSIDS.forward) {
setTimeout(() => {
appForward.close();
appSidebarRight.forwardTab.closeBtn.click();
}, 200);
}
@ -199,7 +199,7 @@ export class AppMediaViewer { @@ -199,7 +199,7 @@ export class AppMediaViewer {
});
const forward = (e: MouseEvent) => {
appForward.init([this.currentMessageID]);
appSidebarRight.forwardTab.open([this.currentMessageID]);
};
[this.buttons.forward, this.buttons["menu-forward"]].forEach(el => {
@ -853,8 +853,8 @@ export class AppMediaViewer { @@ -853,8 +853,8 @@ export class AppMediaViewer {
this.needLoadMore = needLoadMore;
//this.loadMore = loadMore;
if(appForward.container.classList.contains('active')) {
appForward.close();
if(appSidebarRight.historyTabIDs.slice(-1)[0] == AppSidebarRight.SLIDERITEMSIDS.forward) {
appSidebarRight.forwardTab.closeBtn.click();
await new Promise((resolve) => setTimeout(resolve, 200));
}
}

2
src/lib/appManagers/appSidebarLeft.ts

@ -114,6 +114,8 @@ export class AppSidebarLeft extends SidebarSlider { @@ -114,6 +114,8 @@ export class AppSidebarLeft extends SidebarSlider {
[AppSidebarLeft.SLIDERITEMSIDS.includedChats]: includedChatsTab,
});
//this._selectTab(0); // make first tab as default
this.searchInput = new SearchInput('Telegram Search');
this.sidebarEl.querySelector('.item-main .sidebar-header').append(this.searchInput.container);

836
src/lib/appManagers/appSidebarRight.ts

@ -1,68 +1,25 @@ @@ -1,68 +1,25 @@
import { putPreloader, renderImageFromUrl } from "../../components/misc";
//import Scrollable from '../../components/scrollable';
import Scrollable from '../../components/scrollable_new';
import { $rootScope } from "../utils";
import appMessagesManager from "./appMessagesManager";
import appPhotosManager from "./appPhotosManager";
import appPeersManager from "./appPeersManager";
import appUsersManager from "./appUsersManager";
import appProfileManager from "./appProfileManager";
import { RichTextProcessor } from "../richtextprocessor";
import { logger, LogLevels } from "../logger";
import appImManager from "./appImManager";
import appMediaViewer from "./appMediaViewer";
import LazyLoadQueue from "../../components/lazyLoadQueue";
import { wrapDocument, wrapAudio } from "../../components/wrappers";
import AppSearch, { SearchGroup } from "../../components/appSearch";
import AvatarElement from "../../components/avatar";
import appForward from "../../components/appForward";
import SidebarSlider from "../../components/slider";
import SearchInput from "../../components/searchInput";
import { horizontalMenu } from "../../components/horizontalMenu";
import AppStickersTab from "../../components/sidebarRight/stickers";
import AppPollResultsTab from "../../components/sidebarRight/pollResults";
import AppGifsTab from "../../components/sidebarRight/gifs";
import mediaSizes, { ScreenSize } from "../../helpers/mediaSizes";
import { isSafari } from "../../helpers/userAgent";
const testScroll = false;
import AppPrivateSearchTab from "../../components/sidebarRight/search";
import AppSharedMediaTab from "../../components/sidebarRight/sharedMedia";
import AppForwardTab from "../../components/sidebarRight/forward";
const COLUMN_ACTIVE_CLASSNAME = 'is-right-column-shown';
let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => {
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
el.style.display = '';
});
};
const sharedMediaTab = new AppSharedMediaTab();
const searchTab = new AppPrivateSearchTab();
const forwardTab = new AppForwardTab();
const stickersTab = new AppStickersTab();
const pollResultsTab = new AppPollResultsTab();
const gifsTab = new AppGifsTab();
type ContentType = 'contentMembers' | 'contentMedia' | 'contentDocuments' | 'contentLinks' | 'contentAudio';
type SharedMediaType = 'inputMessagesFilterContacts' | 'inputMessagesFilterPhotoVideo' | 'inputMessagesFilterDocument' | 'inputMessagesFilterUrl' | 'inputMessagesFilterMusic';
const contentToSharedMap: {[contentType in ContentType]: SharedMediaType} = {
contentMembers: 'inputMessagesFilterContacts',
contentMedia: 'inputMessagesFilterPhotoVideo',
contentDocuments: 'inputMessagesFilterDocument',
contentLinks: 'inputMessagesFilterUrl',
contentAudio: 'inputMessagesFilterMusic'
};
// TODO: отправленное сообщение с картинкой, или же новое полученное апдейтом сообщение не отобразится в медии
// TODO: по-хорошему, нужно просто сделать апдейты для всего сайдбара
export class AppSidebarRight extends SidebarSlider {
public static SLIDERITEMSIDS = {
sharedMedia: 0,
search: 1,
forward: 2,
stickers: 3,
@ -70,197 +27,32 @@ export class AppSidebarRight extends SidebarSlider { @@ -70,197 +27,32 @@ export class AppSidebarRight extends SidebarSlider {
gifs: 5,
};
public profileContainer: HTMLDivElement;
public profileContentEl: HTMLDivElement;
public contentContainer: HTMLDivElement;
public profileElements: {
avatar: AvatarElement,
name: HTMLDivElement,
subtitle: HTMLDivElement,
bio: HTMLDivElement,
username: HTMLDivElement,
phone: HTMLDivElement,
notificationsRow: HTMLDivElement,
notificationsCheckbox: HTMLInputElement,
notificationsStatus: HTMLParagraphElement
} = {} as any;
public sharedMedia: {
[t in ContentType]: HTMLDivElement
} = {} as any;
private loadSidebarMediaPromises: {[type: string]: Promise<void>} = {};
private loadedAllMedia: {[type: string]: boolean} = {};
public sharedMediaTypes: SharedMediaType[] = [
//'members',
'inputMessagesFilterContacts',
'inputMessagesFilterPhotoVideo',
'inputMessagesFilterDocument',
'inputMessagesFilterUrl',
'inputMessagesFilterMusic'
];
public sharedMediaType: SharedMediaType = 'inputMessagesFilterPhotoVideo';
private sharedMediaSelected: HTMLDivElement = null;
private lazyLoadQueue = new LazyLoadQueue();
public historiesStorage: {
[peerID: number]: Partial<{
[type in SharedMediaType]: number[]
}>
} = {};
public usedFromHistory: Partial<{
[type in SharedMediaType]: number
}> = {};
private log = logger('SR', LogLevels.error);
private peerID = 0;
public scroll: Scrollable = null;
private profileTabs: HTMLUListElement;
private prevTabID = -1;
private mediaDivsByIDs: {
[mid: number]: HTMLDivElement
} = {};
public urlsToRevoke: string[] = [];
private searchContainer: HTMLDivElement;;
public searchCloseBtn: HTMLButtonElement;
private searchInput: SearchInput;
public privateSearch: AppSearch;
private loadMutex: Promise<any> = Promise.resolve();
public sharedMediaTab: AppSharedMediaTab;
public searchTab: AppPrivateSearchTab;
public forwardTab: AppForwardTab;
public stickersTab: AppStickersTab;
public pollResultsTab: AppPollResultsTab;
public gifsTab: AppGifsTab;
constructor() {
super(document.getElementById('column-right') as HTMLElement, {
[AppSidebarRight.SLIDERITEMSIDS.sharedMedia]: sharedMediaTab,
[AppSidebarRight.SLIDERITEMSIDS.search]: searchTab,
[AppSidebarRight.SLIDERITEMSIDS.forward]: forwardTab,
[AppSidebarRight.SLIDERITEMSIDS.stickers]: stickersTab,
[AppSidebarRight.SLIDERITEMSIDS.pollResults]: pollResultsTab,
[AppSidebarRight.SLIDERITEMSIDS.gifs]: gifsTab
});
}, true);
//this._selectTab(3);
this.sharedMediaTab = sharedMediaTab;
this.searchTab = searchTab;
this.forwardTab = forwardTab;
this.stickersTab = stickersTab;
this.pollResultsTab = pollResultsTab;
this.gifsTab = gifsTab;
this.profileContainer = this.sidebarEl.querySelector('.profile-container');
this.profileContentEl = this.sidebarEl.querySelector('.profile-content');
this.contentContainer = this.sidebarEl.querySelector('.content-container');
this.profileElements = {
avatar: this.profileContentEl.querySelector('.profile-avatar'),
name: this.profileContentEl.querySelector('.profile-name'),
subtitle: this.profileContentEl.querySelector('.profile-subtitle'),
bio: this.profileContentEl.querySelector('.profile-row-bio'),
username: this.profileContentEl.querySelector('.profile-row-username'),
phone: this.profileContentEl.querySelector('.profile-row-phone'),
notificationsRow: this.profileContentEl.querySelector('.profile-row-notifications'),
notificationsCheckbox: this.profileContentEl.querySelector('#profile-notifications'),
notificationsStatus: this.profileContentEl.querySelector('.profile-row-notifications > p')
};
this.sharedMedia = {
contentMembers: this.profileContentEl.querySelector('#content-members'),
contentMedia: this.profileContentEl.querySelector('#content-media'),
contentDocuments: this.profileContentEl.querySelector('#content-docs'),
contentLinks: this.profileContentEl.querySelector('#content-links'),
contentAudio: this.profileContentEl.querySelector('#content-audio'),
};
this.searchContainer = this.sidebarEl.querySelector('#search-private-container');
this.searchCloseBtn = this.searchContainer.querySelector('.sidebar-close-button');
this.searchInput = new SearchInput('Search');
this.searchCloseBtn.parentElement.append(this.searchInput.container);
this.privateSearch = new AppSearch(this.searchContainer.querySelector('.chats-container'), this.searchInput, {
messages: new SearchGroup('Private Search', 'messages')
});
let container = this.profileContentEl.querySelector('.content-container .tabs-container') as HTMLDivElement;
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs');
this.scroll = new Scrollable(this.profileContainer, 'SR', undefined, 400);
this.scroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && this.sharedMediaSelected.childElementCount/* && false */) {
this.log('onScrolledBottom will load media');
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();
});
let sidebarCloseBtn = this.sidebarEl.querySelector('.sidebar-close-button') as HTMLButtonElement;
sidebarCloseBtn.addEventListener('click', () => {
this.toggleSidebar(false);
});
this.searchCloseBtn.addEventListener('click', () => {
this.searchContainer.classList.remove('active');
this.privateSearch.reset();
});
this.sharedMedia.contentMedia.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.getMessage(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};
});
appMediaViewer.openMedia(message, target, false, this.sidebarEl, targets.slice(idx + 1).reverse(), targets.slice(0, idx).reverse(), true);
});
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
//let checked = this.profileElements.notificationsCheckbox.checked;
appImManager.mutePeer(this.peerID);
});
mediaSizes.addListener('changeScreen', (from, to) => {
if(from !== undefined && to == ScreenSize.medium) {
this.toggleSidebar(false);
@ -268,29 +60,24 @@ export class AppSidebarRight extends SidebarSlider { @@ -268,29 +60,24 @@ export class AppSidebarRight extends SidebarSlider {
});
}
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`;
public onCloseTab(id: number) {
if(!this.historyTabIDs.length) {
this.toggleSidebar(false);
}
};
private onTransitionEnd = () => {
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
const container = this.scroll.container;
container.style.overflowY = '';
container.style.paddingRight = '0';
};
public beginSearch() {
this.toggleSidebar(true);
this.searchContainer.classList.add('active');
this.privateSearch.beginSearch(this.peerID);
super.onCloseTab(id);
}
/* public selectTab(id: number) {
const res = super.selectTab(id);
if(id !== -1) {
this.toggleSidebar(true);
}
return res;
} */
public toggleSidebar(enable?: boolean) {
/////this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl));
@ -310,6 +97,10 @@ export class AppSidebarRight extends SidebarSlider { @@ -310,6 +97,10 @@ export class AppSidebarRight extends SidebarSlider {
if(!willChange) return Promise.resolve();
if(!active && !this.historyTabIDs.length) {
this.selectTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia);
}
//console.log('sidebar selectTab', enable, willChange);
if(mediaSizes.isMobile) {
appImManager.selectTab(active ? 1 : 2);
@ -354,10 +145,10 @@ export class AppSidebarRight extends SidebarSlider { @@ -354,10 +145,10 @@ export class AppSidebarRight extends SidebarSlider {
(item.element.firstElementChild as HTMLElement).style.display = '';
}
if(active) {
/* if(active) {
appForward.close();
this.searchCloseBtn.click();
}
} */
resolve();
}, 200);
@ -374,559 +165,6 @@ export class AppSidebarRight extends SidebarSlider { @@ -374,559 +165,6 @@ export class AppSidebarRight extends SidebarSlider {
}
});
}
public filterMessagesByType(ids: number[], type: string) {
let messages: any[] = [];
for(let mid of ids) {
let message = appMessagesManager.getMessage(mid);
if(message.media) messages.push(message);
}
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 || message.media.document.type == 'voice' || message.media.document.type == 'audio') {
continue;
}
let doc = message.media.document;
if(doc.attributes) {
if(doc.attributes.find((a: any) => a._ == "documentAttributeSticker")) {
continue;
}
}
filtered.push(message);
}
break;
}
case 'inputMessagesFilterUrl': {
for(let message of messages) {
if(!message.media.webpage || message.media.webpage._ == 'webPageEmpty') {
continue;
}
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) {
const peerID = this.peerID;
const elemsToAppend: HTMLElement[] = [];
const promises: Promise<any>[] = [];
let sharedMediaDiv: HTMLDivElement;
/* 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': {
sharedMediaDiv = this.sharedMedia.contentMedia;
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('media-item');
//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($rootScope.selectedPeerID != peerID) {
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('media-image', '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('media-image');
if(needBlur) img.style.opacity = '0';
div.append(img);
if(isDownloaded || willHaveThumb) {
const promise = new Promise((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 'inputMessagesFilterDocument': {
sharedMediaDiv = this.sharedMedia.contentDocuments;
for(let message of messages) {
let div = wrapDocument(message.media.document, true, false, message.mid);
elemsToAppend.push(div);
}
break;
}
case 'inputMessagesFilterUrl': {
sharedMediaDiv = this.sharedMedia.contentLinks;
for(let message of messages) {
let webpage = message.media.webpage;
let div = document.createElement('div');
let previewDiv = document.createElement('div');
previewDiv.classList.add('preview');
//this.log('wrapping webpage', webpage);
previewDiv.innerText = (webpage.title || webpage.description || webpage.url || webpage.display_url).slice(0, 1);
previewDiv.classList.add('empty');
if(webpage.photo) {
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 60, 60))
.then(() => {
if($rootScope.selectedPeerID != peerID) {
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 = webpage.display_url.split('/', 1)[0];
}
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;
}
case 'inputMessagesFilterMusic': {
sharedMediaDiv = this.sharedMedia.contentAudio;
for(let message of messages) {
let div = wrapAudio(message.media.document, true, message.mid);
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.log.warn('peer changed');
return;
}
}
if(elemsToAppend.length) {
sharedMediaDiv.append(...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) {
if(testScroll/* || 1 == 1 */) {
return;
}
this.log('loadSidebarMedia', single, this.peerID, this.loadSidebarMediaPromises);
const peerID = this.peerID;
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes;
typesToLoad = typesToLoad.filter(type => !this.loadedAllMedia[type]);
if(!typesToLoad.length) return;
const loadCount = (appPhotosManager.windowH / 130 | 0) * 3; // 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] = []);
// render from cache
if(history.length && this.usedFromHistory[type] < history.length) {
let messages: any[] = [];
let used = this.usedFromHistory[type];
do {
let ids = history.slice(used, used + loadCount);
this.log('loadSidebarMedia: will render from cache', used, history, ids, loadCount);
used += ids.length;
messages.push(...this.filterMessagesByType(ids, type));
} while(messages.length < loadCount && used < history.length);
// если перебор
if(messages.length > 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;
let ids = !maxID && appMessagesManager.historiesStorage[peerID]
? appMessagesManager.historiesStorage[peerID].history.slice() : [];
maxID = !maxID && ids.length ? ids[ids.length - 1] : maxID;
this.log('loadSidebarMedia: search house of glass pre', type, ids, maxID);
//let loadCount = history.length ? 50 : 15;
return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, loadCount)
.then(value => {
ids = ids.concat(value.history);
history.push(...ids);
this.log('loadSidebarMedia: search house of glass', type, value, ids);
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
return;
}
if(value.history.length < loadCount) {
this.loadedAllMedia[type] = true;
}
this.usedFromHistory[type] = history.length;
//if(ids.length) {
return this.performSearchResult(this.filterMessagesByType(ids, type), type);
//}
}, (err) => {
this.log.error('load error:', err);
}).then(() => {
this.loadSidebarMediaPromises[type] = null;
});
});
return Promise.all(promises);
}
public cleanup() {
this.loadSidebarMediaPromises = {};
this.loadedAllMedia = {};
this.prevTabID = -1;
this.mediaDivsByIDs = {};
this.lazyLoadQueue.clear();
this.sharedMediaTypes.forEach(type => {
this.usedFromHistory[type] = 0;
});
this.sharedMediaType = 'inputMessagesFilterPhotoVideo';
}
public cleanupHTML() {
//this.contentContainer.classList.remove('loaded');
//this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
if(this.urlsToRevoke.length) {
this.urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
});
this.urlsToRevoke.length = 0;
}
(Object.keys(this.sharedMedia) as ContentType[]).forEach(key => {
this.sharedMedia[key].innerHTML = '';
const inputFilter = contentToSharedMap[key];
if(!this.historiesStorage[this.peerID] || !this.historiesStorage[this.peerID][inputFilter]) {
const parent = this.sharedMedia[key].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('media-item');
div.dataset.id = '' + (i / 3 | 0);
//div.innerText = '' + (i / 3 | 0);
this.sharedMedia.contentMedia.append(div);
}
}
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media
}
public setLoadMutex(promise: Promise<any>) {
this.loadMutex = promise;
}
public setPeer(peerID: number) {
this.peerID = peerID;
this.cleanup();
}
public fillProfileElements() {
let peerID = this.peerID = $rootScope.selectedPeerID;
this.cleanupHTML();
this.profileElements.avatar.setAttribute('peer', '' + peerID);
// username
if(peerID != $rootScope.myID) {
let username = appPeersManager.getPeerUsername(peerID);
if(username) {
setText(appPeersManager.getPeerUsername(peerID), this.profileElements.username);
}
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog) {
let muted = false;
if(dialog.notify_settings && dialog.notify_settings.mute_until) {
muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date();
}
appImManager.setMutedState(muted);
}
} else {
window.requestAnimationFrame(() => {
this.profileElements.notificationsRow.style.display = 'none';
});
}
//let membersLi = this.profileTabs.firstElementChild.children[0] as HTMLLIElement;
if(peerID > 0) {
//membersLi.style.display = 'none';
let user = appUsersManager.getUser(peerID);
if(user.phone && peerID != $rootScope.myID) {
setText(user.rPhone, this.profileElements.phone);
}
appProfileManager.getProfile(peerID, true).then(userFull => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
if(userFull.rAbout && peerID != $rootScope.myID) {
setText(userFull.rAbout, this.profileElements.bio);
}
//this.log('userFull', userFull);
});
} else {
//membersLi.style.display = appPeersManager.isBroadcast(peerID) ? 'none' : '';
let chat = appPeersManager.getPeer(peerID);
appProfileManager.getChatFull(chat.id).then((chatFull: any) => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
//this.log('chatInfo res 2:', chatFull);
if(chatFull.about) {
setText(RichTextProcessor.wrapRichText(chatFull.about), this.profileElements.bio);
}
});
}
}
}
const appSidebarRight = new AppSidebarRight();

5
src/lib/utils.ts

@ -16,7 +16,7 @@ export function dT () { @@ -16,7 +16,7 @@ export function dT () {
return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']';
}
export function isInDOM(element: Element, parentNode?: HTMLElement): boolean {
/* export function isInDOM(element: Element, parentNode?: HTMLElement): boolean {
if(!element) {
return false;
}
@ -26,6 +26,9 @@ export function isInDOM(element: Element, parentNode?: HTMLElement): boolean { @@ -26,6 +26,9 @@ export function isInDOM(element: Element, parentNode?: HTMLElement): boolean {
return true;
}
return isInDOM(element.parentNode as HTMLElement, parentNode);
} */
export function isInDOM(element: Element): boolean {
return element?.isConnected;
}
export function checkDragEvent(e: any) {

4
src/scss/partials/_rightSidebar.scss

@ -51,6 +51,10 @@ @@ -51,6 +51,10 @@
position: relative;
flex: 1 1 auto;
}
.btn-icon {
overflow: inherit;
}
}
#forward-container {

Loading…
Cancel
Save