Contacs with filter & forward (no search and load more) & QR log in & minor improvements
This commit is contained in:
parent
0bc57d8ea0
commit
1c63bf30c1
package-lock.jsonpackage.json
src
components
appForward.tsappSearch.tsbubbleGroups.tschatInput.tsemoticonsDropdown.tslazyLoadQueue.tsmisc.tspreloader.tsscrollable_new.tswrappers.ts
lib
appManagers
apiUpdatesManager.tsappChatsManager.tsappDialogsManager.tsappImManager.tsappMessagesManager.tsappPeersManager.tsappPhotosManager.tsappSidebarLeft.tsappSidebarRight.tsappUsersManager.ts
bin_utils.tslottieLoader.tsmtproto
pages
scss
15
package-lock.json
generated
15
package-lock.json
generated
@ -11779,6 +11779,21 @@
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"qr-code-styling": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/qr-code-styling/-/qr-code-styling-1.0.1.tgz",
|
||||
"integrity": "sha512-+pxRN6qMU7CJ9tJHKGfAGZS/KmNjpJFnc5I6lI/cW5/bEC9/bsGO5ADip8ob7/xeSoppH+P73ylA7Kn/07tclA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"qrcode-generator": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"qrcode-generator": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz",
|
||||
"integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==",
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
|
@ -46,6 +46,7 @@
|
||||
"npm": "^6.14.4",
|
||||
"on-build-webpack": "^0.1.0",
|
||||
"pako": "^1.0.11",
|
||||
"qr-code-styling": "^1.0.1",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"streamsaver": "^2.0.4",
|
||||
|
230
src/components/appForward.ts
Normal file
230
src/components/appForward.ts
Normal file
@ -0,0 +1,230 @@
|
||||
import appSidebarRight from "../lib/appManagers/appSidebarRight";
|
||||
import Scrollable from "./scrollable_new";
|
||||
import appProfileManager from "../lib/appManagers/appProfileManager";
|
||||
import { appPeersManager } from "../lib/services";
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import appDialogsManager from "../lib/appManagers/appDialogsManager";
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
import appUsersManager from "../lib/appManagers/appUsersManager";
|
||||
import { $rootScope, findUpTag, findUpClassName, cancelEvent } from "../lib/utils";
|
||||
import { putPreloader } from "./misc";
|
||||
|
||||
class AppSelectPeers {
|
||||
public container = document.createElement('div');
|
||||
private chatList = document.createElement('ul');
|
||||
private chatsContainer = document.createElement('div');
|
||||
private scrollable: Scrollable;
|
||||
private selectedScrollable: Scrollable;
|
||||
|
||||
private selectedContainer = document.createElement('div');
|
||||
private searchInput = document.createElement('input');
|
||||
|
||||
private selected: {[peerID: number]: HTMLDivElement} = {};
|
||||
|
||||
public freezed = false;
|
||||
|
||||
constructor(private appendTo: HTMLDivElement, private onChange: (length: number) => void) {
|
||||
this.container.classList.add('selector');
|
||||
|
||||
let topContainer = document.createElement('div');
|
||||
topContainer.classList.add('selector-search-container');
|
||||
|
||||
this.selectedContainer.classList.add('selector-search');
|
||||
this.searchInput.placeholder = 'Select chat';
|
||||
this.searchInput.type = 'text';
|
||||
this.selectedContainer.append(this.searchInput);
|
||||
topContainer.append(this.selectedContainer);
|
||||
this.selectedScrollable = new Scrollable(topContainer);
|
||||
|
||||
let delimiter = document.createElement('hr');
|
||||
|
||||
this.chatsContainer.classList.add('chats-container');
|
||||
this.chatsContainer.append(this.chatList);
|
||||
this.scrollable = new Scrollable(this.chatsContainer);
|
||||
|
||||
// в десктопе - сначала без группы, потом архивные, потом контакты без сообщений
|
||||
let offsetIndex = 0;
|
||||
appMessagesManager.getConversations(offsetIndex, 50, 0).then(value => {
|
||||
let dialogs = value.dialogs;
|
||||
let myID = $rootScope.myID;
|
||||
|
||||
offsetIndex = dialogs[value.dialogs.length - 1].index || 0;
|
||||
|
||||
if(dialogs[0].peerID != myID) {
|
||||
dialogs.findAndSplice(d => d.peerID == myID);
|
||||
dialogs.unshift({
|
||||
peerID: myID,
|
||||
pFlags: {}
|
||||
} as any);
|
||||
}
|
||||
|
||||
dialogs.forEach(dialog => {
|
||||
let peerID = dialog.peerID;
|
||||
|
||||
let {dom} = appDialogsManager.addDialog(dialog, this.chatList, false, false);
|
||||
dom.containerEl.insertAdjacentHTML('afterbegin', '<div class="checkbox"><label><input type="checkbox"><span></span></label></div>');
|
||||
|
||||
let subtitle = '';
|
||||
if(peerID < 0) {
|
||||
subtitle = appChatsManager.getChatMembersString(-peerID);
|
||||
} else if(peerID == myID) {
|
||||
subtitle = 'chat with yourself';
|
||||
} else {
|
||||
subtitle = appUsersManager.getUserStatusString(peerID);
|
||||
if(subtitle == 'online') {
|
||||
subtitle = `<i>${subtitle}</i>`;
|
||||
}
|
||||
}
|
||||
|
||||
dom.lastMessageSpan.innerHTML = subtitle;
|
||||
});
|
||||
});
|
||||
|
||||
this.chatList.addEventListener('click', (e) => {
|
||||
let target = e.target as HTMLElement;
|
||||
cancelEvent(e);
|
||||
|
||||
if(this.freezed) return;
|
||||
|
||||
if(target.tagName != 'LI') {
|
||||
target = findUpTag(target, 'LI');
|
||||
}
|
||||
|
||||
if(!target) return;
|
||||
|
||||
let peerID = +target.getAttribute('data-peerID');
|
||||
target.classList.toggle('active');
|
||||
if(peerID in this.selected) {
|
||||
this.remove(peerID);
|
||||
} else {
|
||||
this.add(peerID);
|
||||
}
|
||||
|
||||
let checkbox = target.querySelector('input') as HTMLInputElement;
|
||||
checkbox.checked = !checkbox.checked;
|
||||
});
|
||||
|
||||
this.selectedContainer.addEventListener('click', (e) => {
|
||||
if(this.freezed) return;
|
||||
let target = e.target as HTMLElement;
|
||||
target = findUpClassName(target, 'selector-user');
|
||||
|
||||
if(!target) return;
|
||||
|
||||
let peerID = target.dataset.peerID;
|
||||
let li = this.chatList.querySelector('[data-peerid="' + peerID + '"]') as HTMLElement;
|
||||
li.click();
|
||||
});
|
||||
|
||||
this.container.append(topContainer, delimiter, this.chatsContainer);
|
||||
appendTo.append(this.container);
|
||||
}
|
||||
|
||||
private add(peerID: number) {
|
||||
let div = document.createElement('div');
|
||||
div.classList.add('selector-user', 'scale-in');
|
||||
div.dataset.peerID = '' + peerID;
|
||||
this.selected[peerID] = div;
|
||||
|
||||
let title = appPeersManager.getPeerTitle(peerID, false, true);
|
||||
|
||||
let avatarDiv = document.createElement('div');
|
||||
avatarDiv.classList.add('user-avatar', 'tgico');
|
||||
appProfileManager.putPhoto(avatarDiv, peerID);
|
||||
|
||||
div.innerHTML = title;
|
||||
div.insertAdjacentElement('afterbegin', avatarDiv);
|
||||
|
||||
this.selectedContainer.insertBefore(div, this.searchInput);
|
||||
this.selectedScrollable.scrollTop = this.selectedScrollable.scrollHeight;
|
||||
this.onChange(Object.keys(this.selected).length);
|
||||
}
|
||||
|
||||
private remove(peerID: number) {
|
||||
let div = this.selected[peerID];
|
||||
div.classList.remove('scale-in');
|
||||
void div.offsetWidth;
|
||||
div.classList.add('scale-out');
|
||||
div.addEventListener('animationend', () => {
|
||||
delete this.selected[peerID];
|
||||
div.remove();
|
||||
this.onChange(Object.keys(this.selected).length);
|
||||
}, {once: true});
|
||||
}
|
||||
|
||||
public getSelected() {
|
||||
return Object.keys(this.selected).map(p => +p);
|
||||
}
|
||||
}
|
||||
|
||||
class AppForward {
|
||||
private 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;
|
||||
|
||||
private selector: AppSelectPeers;
|
||||
private msgIDs: number[] = [];
|
||||
|
||||
constructor() {
|
||||
this.closeBtn.addEventListener('click', () => {
|
||||
this.cleanup();
|
||||
this.container.classList.remove('active');
|
||||
appSidebarRight.onSidebarScroll();
|
||||
});
|
||||
|
||||
this.sendBtn.addEventListener('click', () => {
|
||||
let peerIDs = this.selector.getSelected();
|
||||
|
||||
if(this.msgIDs.length && peerIDs.length) {
|
||||
this.sendBtn.classList.remove('tgico-send');
|
||||
this.sendBtn.disabled = true;
|
||||
putPreloader(this.sendBtn);
|
||||
this.selector.freezed = true;
|
||||
|
||||
let s = () => {
|
||||
let promises = peerIDs.splice(0, 3).map(peerID => {
|
||||
return appMessagesManager.forwardMessages(peerID, this.msgIDs);
|
||||
});
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
if(peerIDs.length) {
|
||||
return s();
|
||||
} else {
|
||||
this.closeBtn.click();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
s();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
if(this.selector) {
|
||||
this.selector.container.remove();
|
||||
this.selector = null;
|
||||
}
|
||||
}
|
||||
|
||||
public init(ids: number[]) {
|
||||
this.cleanup();
|
||||
this.msgIDs = ids;
|
||||
|
||||
appSidebarRight.toggleSidebar(true);
|
||||
this.container.classList.add('active');
|
||||
this.sendBtn.innerHTML = '';
|
||||
this.sendBtn.classList.add('tgico-send');
|
||||
this.sendBtn.disabled = false;
|
||||
|
||||
this.selector = new AppSelectPeers(this.container, (length) => {
|
||||
if(length) {
|
||||
this.sendBtn.classList.add('is-visible');
|
||||
} else {
|
||||
this.sendBtn.classList.remove('is-visible');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new AppForward();
|
@ -4,17 +4,19 @@ import appMessagesIDsManager from "../lib/appManagers/appMessagesIDsManager";
|
||||
import appUsersManager from "../lib/appManagers/appUsersManager";
|
||||
import appPeersManager from '../lib/appManagers/appPeersManager';
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import { numberWithCommas, escapeRegExp } from "../lib/utils";
|
||||
import { escapeRegExp } from "../lib/utils";
|
||||
import { formatPhoneNumber } from "./misc";
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
|
||||
export class SearchGroup {
|
||||
container: HTMLDivElement;
|
||||
nameEl: HTMLDivElement;
|
||||
list: HTMLUListElement;
|
||||
|
||||
constructor(public name: string, public type: string) {
|
||||
constructor(public name: string, public type: string, private clearable = true, className?: string) {
|
||||
this.list = document.createElement('ul');
|
||||
this.container = document.createElement('div');
|
||||
if(className) this.container.classList.add(className);
|
||||
this.nameEl = document.createElement('div');
|
||||
this.nameEl.classList.add('search-group__name');
|
||||
this.nameEl.innerText = name;
|
||||
@ -28,7 +30,10 @@ export class SearchGroup {
|
||||
|
||||
clear() {
|
||||
this.container.style.display = 'none';
|
||||
this.list.innerHTML = '';
|
||||
|
||||
if(this.clearable) {
|
||||
this.list.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
setActive() {
|
||||
@ -47,7 +52,7 @@ export default class AppSearch {
|
||||
|
||||
private query = '';
|
||||
|
||||
private listsContainer: HTMLDivElement = null;
|
||||
public listsContainer: HTMLDivElement = null;
|
||||
|
||||
private peerID = 0; // 0 - means global
|
||||
|
||||
@ -155,11 +160,7 @@ export default class AppSearch {
|
||||
if(showMembersCount && (peer.participants_count || peer.participants)) {
|
||||
let regExp = new RegExp(`(${escapeRegExp(query)})`, 'gi');
|
||||
dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>');
|
||||
|
||||
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
|
||||
let participants_count = peer.participants_count || peer.participants.participants.length;
|
||||
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
|
||||
dom.lastMessageSpan.innerText = subtitle;
|
||||
dom.lastMessageSpan.innerText = appChatsManager.getChatMembersString(-peerID);
|
||||
} else {
|
||||
let username = appPeersManager.getPeerUsername(peerID);
|
||||
if(!username) {
|
||||
|
@ -154,6 +154,7 @@ export default class BubbleGroups {
|
||||
|
||||
cleanup() {
|
||||
this.bubblesByGroups = [];
|
||||
this.groups = [];
|
||||
/* for(let value of this.updateRAFs.values()) {
|
||||
window.cancelAnimationFrame(value);
|
||||
}
|
||||
|
@ -393,7 +393,7 @@ export class ChatInput {
|
||||
if(!file) continue;
|
||||
|
||||
willAttach.type = file.type.indexOf('image/') === 0 ? 'media' : "document";
|
||||
attachFile(file);
|
||||
attachFiles([file]);
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
@ -559,12 +559,12 @@ export class ChatInput {
|
||||
this.onMessageSent(!this.editMsgID);
|
||||
};
|
||||
|
||||
public setTopInfo(title: string, subtitle: string, input?: string, media?: any) {
|
||||
public setTopInfo(title: string, subtitle: string, input?: string, message?: any) {
|
||||
//appImManager.scrollPosition.prepareFor('down');
|
||||
|
||||
if(this.replyElements.container.lastElementChild.tagName == 'DIV') {
|
||||
this.replyElements.container.lastElementChild.remove();
|
||||
this.replyElements.container.append(wrapReply(title, subtitle, media));
|
||||
this.replyElements.container.append(wrapReply(title, subtitle, message));
|
||||
}
|
||||
//this.replyElements.titleEl.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : '';
|
||||
//this.replyElements.subtitleEl.innerHTML = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : '';
|
||||
|
@ -141,6 +141,15 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
||||
let div = document.createElement('div');
|
||||
div.classList.add('emoji-category');
|
||||
|
||||
let titleDiv = document.createElement('div');
|
||||
titleDiv.classList.add('category-title');
|
||||
titleDiv.innerText = category;
|
||||
|
||||
let itemsDiv = document.createElement('div');
|
||||
itemsDiv.classList.add('category-items');
|
||||
|
||||
div.append(titleDiv, itemsDiv);
|
||||
|
||||
let emojis = sorted[category];
|
||||
emojis.forEach(details => {
|
||||
let emoji = details.unified;
|
||||
@ -161,7 +170,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
||||
|
||||
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
|
||||
//spanEmoji.setAttribute('emoji', emoji);
|
||||
div.appendChild(spanEmoji);
|
||||
itemsDiv.appendChild(spanEmoji);
|
||||
});
|
||||
|
||||
divs[category] = div;
|
||||
@ -272,17 +281,23 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
||||
let heights: number[] = [];
|
||||
|
||||
let heightRAF = 0;
|
||||
let categoryPush = (categoryDiv: HTMLDivElement, docs: MTDocument[], prepend?: boolean) => {
|
||||
let categoryPush = (categoryDiv: HTMLDivElement, categoryTitle: string, docs: MTDocument[], prepend?: boolean) => {
|
||||
//if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
|
||||
|
||||
let container = document.createElement('div');
|
||||
categoryDiv.append(container);
|
||||
let itemsDiv = document.createElement('div');
|
||||
itemsDiv.classList.add('category-items');
|
||||
|
||||
let titleDiv = document.createElement('div');
|
||||
titleDiv.classList.add('category-title');
|
||||
titleDiv.innerText = categoryTitle;
|
||||
|
||||
categoryDiv.append(titleDiv, itemsDiv);
|
||||
|
||||
docs.forEach(doc => {
|
||||
let div = document.createElement('div');
|
||||
wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true, false, true);
|
||||
|
||||
container.append(div);
|
||||
itemsDiv.append(div);
|
||||
});
|
||||
|
||||
if(prepend) stickersScroll.prepend(categoryDiv);
|
||||
@ -356,7 +371,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
||||
|
||||
//stickersScroll.prepend(categoryDiv);
|
||||
|
||||
categoryPush(categoryDiv, stickers.stickers, true);
|
||||
categoryPush(categoryDiv, 'Recent', stickers.stickers, true);
|
||||
}),
|
||||
|
||||
apiManager.invokeApi('messages.getAllStickers', {hash: 0}).then(async(res) => {
|
||||
@ -413,7 +428,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
||||
wrapSticker(stickerSet.documents[0], li as any, undefined, undefined, EMOTICONSSTICKERGROUP); // kostil
|
||||
}
|
||||
|
||||
categoryPush(categoryDiv, stickerSet.documents, false);
|
||||
categoryPush(categoryDiv, stickerSet.set.title, stickerSet.documents, false);
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
@ -15,6 +15,7 @@ export default class LazyLoadQueue {
|
||||
private unlockResolve: () => void = null;
|
||||
|
||||
private log = console.log.bind(console, '[LL]:');
|
||||
private debug = false;
|
||||
|
||||
constructor(private parallelLimit = 5) {
|
||||
|
||||
@ -69,13 +70,14 @@ export default class LazyLoadQueue {
|
||||
|
||||
let tempID = this.tempID;
|
||||
|
||||
this.log('will load media', this.lockPromise, item);
|
||||
this.debug && this.log('will load media', this.lockPromise, item);
|
||||
|
||||
try {
|
||||
if(this.lockPromise) {
|
||||
let perf = performance.now();
|
||||
await this.lockPromise;
|
||||
this.log('waited lock:', performance.now() - perf);
|
||||
await this.lockPromise;
|
||||
|
||||
this.debug && this.log('waited lock:', performance.now() - perf);
|
||||
}
|
||||
|
||||
await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
|
||||
@ -88,7 +90,7 @@ export default class LazyLoadQueue {
|
||||
this.loadingMedia--;
|
||||
}
|
||||
|
||||
this.log('loaded media');
|
||||
this.debug && this.log('loaded media');
|
||||
|
||||
if(this.lazyLoadMedia.length) {
|
||||
this.processQueue();
|
||||
|
@ -107,10 +107,13 @@ let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceEl
|
||||
};
|
||||
|
||||
export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceElement, url: string) {
|
||||
if(loadedURLs[url]) return set(elem, url);
|
||||
if(loadedURLs[url]) {
|
||||
set(elem, url);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(elem instanceof HTMLSourceElement) {
|
||||
return elem.src = url;
|
||||
elem.src = url;
|
||||
} else {
|
||||
let loader = new Image();
|
||||
loader.src = url;
|
||||
@ -119,6 +122,8 @@ export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGIma
|
||||
loadedURLs[url] = true;
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function putPreloader(elem: Element, returnDiv = false) {
|
||||
|
@ -49,11 +49,14 @@ export default class ProgressivePreloader {
|
||||
this.promise = promise;
|
||||
|
||||
let tempID = --this.tempID;
|
||||
promise.then(() => {
|
||||
let onEnd = () => {
|
||||
if(tempID == this.tempID) {
|
||||
this.detach();
|
||||
}
|
||||
});
|
||||
|
||||
promise.notify = null;
|
||||
};
|
||||
promise.then(onEnd, onEnd);
|
||||
|
||||
promise.notify = (details: {done: number, total: number}) => {
|
||||
if(tempID != this.tempID) return;
|
||||
|
@ -64,7 +64,7 @@ export default class Scrollable {
|
||||
private virtualTempIDBottom = 0;
|
||||
private lastTopID = 0;
|
||||
private lastBottomID = 0;
|
||||
private lastScrollDirection = true; // true = bottom
|
||||
private lastScrollDirection = 0; // true = bottom
|
||||
|
||||
private setVisible(element: HTMLElement) {
|
||||
if(this.visible.has(element)) return;
|
||||
@ -116,11 +116,11 @@ export default class Scrollable {
|
||||
//this.debug && this.log('intersection entry:', entry, isTop, isBottom, this.lastTopID, this.lastBottomID);
|
||||
});
|
||||
|
||||
if(!filtered.length) {
|
||||
if(!filtered.length || this.lastScrollDirection === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.lastScrollDirection) { // bottom
|
||||
if(this.lastScrollDirection === 1) { // bottom
|
||||
let target = filtered[filtered.length - 1].target as HTMLElement;
|
||||
this.lastBottomID = +target.dataset.virtual;
|
||||
|
||||
@ -300,6 +300,7 @@ export default class Scrollable {
|
||||
|
||||
this.disableHoverTimeout = setTimeout(() => {
|
||||
appendTo.classList.remove('disable-hover');
|
||||
this.lastScrollDirection = 0;
|
||||
|
||||
if(!this.measureMutex.isFulfilled) {
|
||||
this.measureMutex.resolve();
|
||||
@ -346,8 +347,12 @@ export default class Scrollable {
|
||||
}
|
||||
}
|
||||
|
||||
this.lastScrollDirection = this.lastScrollTop < scrollTop;
|
||||
this.lastScrollTop = scrollTop;
|
||||
if(this.lastScrollTop != scrollTop) {
|
||||
this.lastScrollDirection = this.lastScrollTop < scrollTop ? 1 : -1;
|
||||
this.lastScrollTop = scrollTop;
|
||||
} else {
|
||||
this.lastScrollDirection = 0;
|
||||
}
|
||||
this.onScrollMeasure = 0;
|
||||
});
|
||||
}
|
||||
|
@ -115,6 +115,23 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
|
||||
let source = document.createElement('source');
|
||||
video.append(source);
|
||||
|
||||
let span: HTMLSpanElement;
|
||||
if(doc.type != 'round') {
|
||||
span = document.createElement('span');
|
||||
span.classList.add('video-time');
|
||||
container.append(span);
|
||||
|
||||
if(doc.type != 'gif') {
|
||||
span.innerText = (doc.duration + '').toHHMMSS(false);
|
||||
|
||||
let spanPlay = document.createElement('span');
|
||||
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
|
||||
container.append(spanPlay);
|
||||
} else {
|
||||
span.innerText = 'GIF';
|
||||
}
|
||||
}
|
||||
|
||||
let loadVideo = () => {
|
||||
let promise = appDocsManager.downloadDoc(doc);
|
||||
@ -133,7 +150,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
|
||||
//return;
|
||||
|
||||
console.log('loaded doc:', doc, doc.url, blob, container);
|
||||
//console.log('loaded doc:', doc, doc.url, blob, container);
|
||||
|
||||
renderImageFromUrl(source, doc.url);
|
||||
source.type = doc.mime_type;
|
||||
@ -164,7 +181,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
downloadDiv.classList.add('download');
|
||||
|
||||
let span = document.createElement('span');
|
||||
span.classList.add('tgico-download');
|
||||
span.classList.add('btn-circle', 'tgico-download');
|
||||
downloadDiv.append(span);
|
||||
|
||||
downloadDiv.addEventListener('click', () => {
|
||||
@ -172,7 +189,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
loadVideo();
|
||||
});
|
||||
|
||||
container.append(downloadDiv);
|
||||
container.prepend(downloadDiv);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -642,7 +659,7 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme
|
||||
}
|
||||
}
|
||||
|
||||
console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||
|
||||
// так нельзя делать, потому что может быть загружен неправильный размер картинки
|
||||
/* if(photo.downloaded && photo.url) {
|
||||
@ -727,6 +744,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
|
||||
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
|
||||
}
|
||||
|
||||
let downloaded = doc.downloaded;
|
||||
let load = () => appDocsManager.downloadDoc(doc.id).then(blob => {
|
||||
//console.log('loaded sticker:', blob, div);
|
||||
if(middleware && !middleware()) return;
|
||||
@ -803,6 +821,16 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
|
||||
} else if(stickerType == 1) {
|
||||
let img = new Image();
|
||||
|
||||
if(!downloaded && (!div.firstElementChild || div.firstElementChild.tagName != 'IMG')) {
|
||||
img.style.opacity = '' + 0;
|
||||
|
||||
img.onload = () => {
|
||||
window.requestAnimationFrame(() => {
|
||||
img.style.opacity = '';
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if(!doc.url) {
|
||||
appWebpManager.polyfillImage(img, blob).then((url) => {
|
||||
doc.url = url;
|
||||
@ -825,7 +853,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
|
||||
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat'}), Promise.resolve()) : load();
|
||||
}
|
||||
|
||||
export function wrapReply(title: string, subtitle: string, media?: any) {
|
||||
export function wrapReply(title: string, subtitle: string, message?: any) {
|
||||
let div = document.createElement('div');
|
||||
div.classList.add('reply');
|
||||
|
||||
@ -843,11 +871,15 @@ export function wrapReply(title: string, subtitle: string, media?: any) {
|
||||
|
||||
replyTitle.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : '';
|
||||
|
||||
let media = message && message.media;
|
||||
if(media) {
|
||||
if(media.photo) {
|
||||
if(message.grouped_id) {
|
||||
replySubtitle.innerHTML = 'Album';
|
||||
} else if(media.photo) {
|
||||
replySubtitle.innerHTML = 'Photo';
|
||||
} else if(media.document && media.document.type) {
|
||||
replySubtitle.innerHTML = media.document.type;
|
||||
let type = media.document.type as string;
|
||||
replySubtitle.innerHTML = type.charAt(0).toUpperCase() + type.slice(1); // capitalizeFirstLetter
|
||||
} else if(media.webpage) {
|
||||
replySubtitle.innerHTML = RichTextProcessor.wrapPlainText(media.webpage.url);
|
||||
} else {
|
||||
|
@ -24,8 +24,8 @@ export class ApiUpdatesManager {
|
||||
};
|
||||
|
||||
public channelStates: any = {};
|
||||
|
||||
public myID = 0;
|
||||
private attached = false;
|
||||
|
||||
constructor() {
|
||||
apiManager.getUserID().then((id) => {
|
||||
@ -501,6 +501,9 @@ export class ApiUpdatesManager {
|
||||
}
|
||||
|
||||
public attach() {
|
||||
if(this.attached) return;
|
||||
|
||||
this.attached = true;
|
||||
apiManager.setUpdatesProcessor(this.processUpdateMessage.bind(this));
|
||||
apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult: any) => {
|
||||
this.updatesState.seq = stateResult.seq;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { $rootScope, isObject, SearchIndexManager, safeReplaceObject, copy } from "../utils";
|
||||
import { $rootScope, isObject, SearchIndexManager, safeReplaceObject, copy, numberWithCommas } from "../utils";
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
|
||||
@ -78,6 +78,7 @@ export class AppChatsManager {
|
||||
}
|
||||
|
||||
public getChat(id: number) {
|
||||
if(id < 0) id = -id;
|
||||
return this.chats[id] || {id: id, deleted: true, access_hash: this.channelAccess[id]};
|
||||
}
|
||||
|
||||
@ -204,6 +205,14 @@ export class AppChatsManager {
|
||||
return 'g' + id;
|
||||
}
|
||||
|
||||
public getChatMembersString(id: number) {
|
||||
let chat = this.getChat(id);
|
||||
|
||||
let isChannel = this.isChannel(id) && !this.isMegagroup(id);
|
||||
let participants_count = chat.participants_count || chat.participants.participants.length;
|
||||
return numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
|
||||
}
|
||||
|
||||
public wrapForFull(id: number, fullChat: any) {
|
||||
var chatFull = copy(fullChat);
|
||||
var chat = this.getChat(id);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { langPack, findUpClassName, $rootScope, escapeRegExp, whichChild } from "../utils";
|
||||
import appImManager, { AppImManager } from "./appImManager";
|
||||
import appPeersManager from './appPeersManager';
|
||||
import appMessagesManager, { AppMessagesManager } from "./appMessagesManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
import appMessagesManager, { AppMessagesManager, Dialog } from "./appMessagesManager";
|
||||
import appUsersManager, { User } from "./appUsersManager";
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import { ripple, putPreloader } from "../../components/misc";
|
||||
//import Scrollable from "../../components/scrollable";
|
||||
@ -208,7 +208,7 @@ export class AppDialogsManager {
|
||||
console.time('getDialogs time');
|
||||
|
||||
let loadCount = 50/*this.chatsLoadCount */;
|
||||
this.loadDialogsPromise = appMessagesManager.getConversations('', offset, loadCount, +archived);
|
||||
this.loadDialogsPromise = appMessagesManager.getConversations(offset, loadCount, +archived);
|
||||
|
||||
let result = await this.loadDialogsPromise;
|
||||
|
||||
@ -535,8 +535,8 @@ export class AppDialogsManager {
|
||||
let d = [];
|
||||
|
||||
d.push(duration % 60 + ' s');
|
||||
if(duration > 60) d.push((duration / 60 | 0) + ' min');
|
||||
//if(duration > 3600) d.push((duration / 3600 | 0) + ' h');
|
||||
if(duration >= 60) d.push((duration / 60 | 0) + ' min');
|
||||
//if(duration >= 3600) d.push((duration / 3600 | 0) + ' h');
|
||||
suffix = ' (' + d.reverse().join(' ') + ')';
|
||||
}
|
||||
}
|
||||
@ -674,22 +674,33 @@ export class AppDialogsManager {
|
||||
return this.doms[peerID] || this.domsArchived[peerID];
|
||||
}
|
||||
|
||||
public addDialog(dialog: {
|
||||
peerID: number,
|
||||
pFlags: any,
|
||||
peer: any,
|
||||
folder_id?: number
|
||||
}, container?: HTMLUListElement, drawStatus = true) {
|
||||
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement, drawStatus = true, rippleEnabled = true, onlyFirstName = false) {
|
||||
let dialog: Dialog;
|
||||
|
||||
if(typeof(_dialog) === 'number') {
|
||||
let originalDialog = appMessagesManager.getDialogByPeerID(_dialog)[0];
|
||||
if(!originalDialog) {
|
||||
originalDialog = {
|
||||
peerID: _dialog,
|
||||
pFlags: {}
|
||||
} as any;
|
||||
}
|
||||
|
||||
dialog = originalDialog;
|
||||
} else {
|
||||
dialog = _dialog;
|
||||
}
|
||||
|
||||
let peerID: number = dialog.peerID;
|
||||
|
||||
if((this.doms[peerID] || this.domsArchived[peerID]) && !container) return;
|
||||
|
||||
let title = appPeersManager.getPeerTitle(peerID);
|
||||
let title = appPeersManager.getPeerTitle(peerID, false, onlyFirstName);
|
||||
|
||||
let avatarDiv = document.createElement('div');
|
||||
avatarDiv.classList.add('user-avatar');
|
||||
|
||||
if(drawStatus && peerID != $rootScope.myID) {
|
||||
if(drawStatus && peerID != $rootScope.myID && dialog.peer) {
|
||||
let peer = dialog.peer;
|
||||
|
||||
switch(peer._) {
|
||||
@ -714,7 +725,7 @@ export class AppDialogsManager {
|
||||
titleSpan.classList.add('user-title');
|
||||
|
||||
if(peerID == $rootScope.myID) {
|
||||
title = 'Saved Messages';
|
||||
title = onlyFirstName ? 'Saved' : 'Saved Messages';
|
||||
}
|
||||
|
||||
//console.log('trying to load photo for:', title);
|
||||
@ -733,21 +744,24 @@ export class AppDialogsManager {
|
||||
paddingDiv.classList.add('rp');
|
||||
paddingDiv.append(avatarDiv, captionDiv);
|
||||
|
||||
ripple(paddingDiv, (id) => {
|
||||
this.log('dialogs click element');
|
||||
this.lastClickID = id;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rippleCallback = resolve;
|
||||
//setTimeout(() => resolve(), 100);
|
||||
//window.requestAnimationFrame(() => window.requestAnimationFrame(() => resolve()));
|
||||
if(rippleEnabled) {
|
||||
ripple(paddingDiv, (id) => {
|
||||
this.log('dialogs click element');
|
||||
this.lastClickID = id;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rippleCallback = resolve;
|
||||
//setTimeout(() => resolve(), 100);
|
||||
//window.requestAnimationFrame(() => window.requestAnimationFrame(() => resolve()));
|
||||
});
|
||||
}, (id) => {
|
||||
//console.log('appDialogsManager: ripple onEnd called!');
|
||||
if(id == this.lastGoodClickID) {
|
||||
appImManager.lazyLoadQueue.unlock();
|
||||
}
|
||||
});
|
||||
}, (id) => {
|
||||
//console.log('appDialogsManager: ripple onEnd called!');
|
||||
if(id == this.lastGoodClickID) {
|
||||
appImManager.lazyLoadQueue.unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let li = document.createElement('li');
|
||||
li.append(paddingDiv);
|
||||
@ -807,22 +821,26 @@ export class AppDialogsManager {
|
||||
return {dom, dialog};
|
||||
}
|
||||
|
||||
public setTyping(dialog: any, user: any) {
|
||||
public setTyping(dialog: Dialog, user: User) {
|
||||
let dom = this.getDialogDom(dialog.peerID);
|
||||
|
||||
let str = '';
|
||||
if(dialog.peerID < 0) {
|
||||
let s = user.rFirstName || user.username;
|
||||
if(!s) return;
|
||||
str = s + ' ';
|
||||
}
|
||||
|
||||
let senderBold = document.createElement('i');
|
||||
if(dialog.peerID < 0) str = (user.first_name || user.last_name || user.username) + ' ';
|
||||
str += 'typing...';
|
||||
senderBold.innerText = str;
|
||||
senderBold.innerHTML = str;
|
||||
|
||||
dom.lastMessageSpan.innerHTML = '';
|
||||
dom.lastMessageSpan.append(senderBold);
|
||||
dom.lastMessageSpan.classList.add('user-typing');
|
||||
}
|
||||
|
||||
public unsetTyping(dialog: any) {
|
||||
public unsetTyping(dialog: Dialog) {
|
||||
let dom = this.getDialogDom(dialog.peerID);
|
||||
dom.lastMessageSpan.classList.remove('user-typing');
|
||||
this.setLastMessage(dialog, null, dom);
|
||||
|
@ -26,6 +26,7 @@ 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';
|
||||
|
||||
console.log('appImManager included!');
|
||||
|
||||
@ -333,6 +334,12 @@ export class AppImManager {
|
||||
|
||||
if(!bubble) return;
|
||||
|
||||
//this.log('chatInner click:', target);
|
||||
if(target.tagName == 'SPAN') {
|
||||
(target.parentElement.querySelector('video') as HTMLElement).click(); // hot-fix for time and play button
|
||||
return;
|
||||
}
|
||||
|
||||
if((target.tagName == 'IMG' && !target.classList.contains('emoji') && !target.parentElement.classList.contains('user-avatar'))
|
||||
|| target.tagName == 'image'
|
||||
|| target.classList.contains('album-item')
|
||||
@ -458,7 +465,9 @@ export class AppImManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if(e.key == 'Meta' || e.key == 'Control') {
|
||||
if(e.key == 'Escape' && this.peerID != 0) { // hide current dialog
|
||||
this.setPeer(0);
|
||||
} else if(e.key == 'Meta' || e.key == 'Control') {
|
||||
return;
|
||||
} else if(e.key == 'c' && (e.ctrlKey || e.metaKey) && target.tagName != 'INPUT') {
|
||||
return;
|
||||
@ -566,14 +575,18 @@ export class AppImManager {
|
||||
|
||||
this.contextMenu.querySelector('.menu-reply').addEventListener('click', () => {
|
||||
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
|
||||
this.chatInputC.setTopInfo(appPeersManager.getPeerTitle(message.fromID, true), message.message, undefined, message.media);
|
||||
this.chatInputC.setTopInfo(appPeersManager.getPeerTitle(message.fromID, true), message.message, undefined, message);
|
||||
this.chatInputC.replyToMsgID = this.contextMenuMsgID;
|
||||
this.chatInputC.editMsgID = 0;
|
||||
});
|
||||
|
||||
this.contextMenu.querySelector('.menu-forward').addEventListener('click', () => {
|
||||
appForward.init([this.contextMenuMsgID]);
|
||||
});
|
||||
|
||||
this.contextMenuEdit.addEventListener('click', () => {
|
||||
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
|
||||
this.chatInputC.setTopInfo('Editing', message.message, message.message, message.media);
|
||||
this.chatInputC.setTopInfo('Editing', message.message, message.message, message);
|
||||
this.chatInputC.replyToMsgID = 0;
|
||||
this.chatInputC.editMsgID = this.contextMenuMsgID;
|
||||
});
|
||||
@ -848,40 +861,11 @@ export class AppImManager {
|
||||
if(this.myID == this.peerID) {
|
||||
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = '';
|
||||
} else if(user && user.status) {
|
||||
let subtitle = '';
|
||||
switch(user.status._) {
|
||||
case 'userStatusRecently': {
|
||||
subtitle += 'last seen recently';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'userStatusOffline': {
|
||||
subtitle = 'last seen ';
|
||||
|
||||
let date = user.status.was_online;
|
||||
let now = Date.now() / 1000;
|
||||
|
||||
if((now - date) < 60) {
|
||||
subtitle += ' just now';
|
||||
} else if((now - date) < 3600) {
|
||||
subtitle += ((now - date) / 60 | 0) + ' minutes ago';
|
||||
} else if(now - date < 86400) {
|
||||
subtitle += ((now - date) / 3600 | 0) + ' hours ago';
|
||||
} else {
|
||||
let d = new Date(date * 1000);
|
||||
subtitle += ('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2) + ' at ' +
|
||||
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
let subtitle = appUsersManager.getUserStatusString(user.id);
|
||||
|
||||
case 'userStatusOnline': {
|
||||
this.subtitleEl.classList.add('online');
|
||||
appSidebarRight.profileElements.subtitle.classList.add('online');
|
||||
subtitle = 'online';
|
||||
break;
|
||||
}
|
||||
if(subtitle == 'online') {
|
||||
this.subtitleEl.classList.add('online');
|
||||
appSidebarRight.profileElements.subtitle.classList.add('online');
|
||||
}
|
||||
|
||||
appSidebarRight.profileElements.subtitle.innerText = subtitle;
|
||||
@ -951,6 +935,10 @@ export class AppImManager {
|
||||
appSidebarRight.toggleSidebar(false);
|
||||
this.topbar.style.display = this.chatInput.style.display = this.goDownBtn.style.display = 'none';
|
||||
this.cleanup();
|
||||
if(appDialogsManager.lastActiveListElement) {
|
||||
appDialogsManager.lastActiveListElement.classList.remove('active');
|
||||
appDialogsManager.lastActiveListElement = null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1087,9 +1075,9 @@ export class AppImManager {
|
||||
return true;
|
||||
})/* .catch(err => {
|
||||
this.log.error(err);
|
||||
}) */,
|
||||
}) *//* ,
|
||||
|
||||
appSidebarRight.fillProfileElements()/* ,
|
||||
appSidebarRight.fillProfileElements() *//* ,
|
||||
appSidebarRight.loadSidebarMedia(true) */
|
||||
]).catch(err => {
|
||||
this.log.error('setPeer promises error:', err);
|
||||
@ -1152,6 +1140,7 @@ export class AppImManager {
|
||||
let bubble = this.bubbles[id];
|
||||
delete this.bubbles[id];
|
||||
|
||||
this.bubbleGroups.removeBubble(bubble, id);
|
||||
this.unreadedObserver.unobserve(bubble);
|
||||
this.scrollable.removeElement(bubble);
|
||||
//bubble.remove();
|
||||
@ -1236,7 +1225,7 @@ export class AppImManager {
|
||||
|
||||
// reverse means top
|
||||
public renderMessage(message: any, reverse = false, multipleRender = false, bubble: HTMLDivElement = null, updatePosition = true) {
|
||||
this.log('message to render:', message);
|
||||
//this.log('message to render:', message);
|
||||
if(message.deleted) return;
|
||||
else if(message.grouped_id) { // will render only last album's message
|
||||
let storage = appMessagesManager.groupedMessagesStorage[message.grouped_id];
|
||||
@ -1614,7 +1603,7 @@ export class AppImManager {
|
||||
case 'messageMediaDocument': {
|
||||
let doc = message.media.document;
|
||||
|
||||
this.log('messageMediaDocument', doc, bubble);
|
||||
//this.log('messageMediaDocument', doc, bubble);
|
||||
|
||||
if(doc.sticker/* && doc.size <= 1e6 */) {
|
||||
bubble.classList.add('sticker');
|
||||
@ -1639,7 +1628,7 @@ export class AppImManager {
|
||||
|
||||
break;
|
||||
} else if(doc.type == 'video' || doc.type == 'gif' || doc.type == 'round'/* && doc.size <= 20e6 */) {
|
||||
this.log('never get free 2', doc);
|
||||
//this.log('never get free 2', doc);
|
||||
|
||||
if(doc.type == 'round') {
|
||||
bubble.classList.add('round');
|
||||
@ -1700,7 +1689,7 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
case 'messageMediaContact': {
|
||||
this.log('wrapping contact', message);
|
||||
//this.log('wrapping contact', message);
|
||||
|
||||
let contactDiv = document.createElement('div');
|
||||
contactDiv.classList.add('contact');
|
||||
@ -1791,7 +1780,7 @@ export class AppImManager {
|
||||
bubble.setAttribute('data-original-mid', message.reply_to_mid);
|
||||
}
|
||||
|
||||
bubbleContainer.append(wrapReply(originalPeerTitle, originalMessage.message || '', originalMessage.media));
|
||||
bubbleContainer.append(wrapReply(originalPeerTitle, originalMessage.message || '', originalMessage));
|
||||
bubble.classList.add('is-reply');
|
||||
}
|
||||
|
||||
@ -2033,7 +2022,7 @@ export class AppImManager {
|
||||
|
||||
let pageCount = this.bubblesContainer.clientHeight / 38/* * 1.25 */ | 0;
|
||||
//let loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount;
|
||||
let realLoadCount = 50;
|
||||
let realLoadCount = Object.keys(this.bubbles).length > 0 ? 40 : pageCount;//let realLoadCount = 50;
|
||||
let loadCount = realLoadCount;
|
||||
|
||||
if(testScroll) {
|
||||
@ -2306,4 +2295,5 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
const appImManager = new AppImManager();
|
||||
(window as any).appImManager = appImManager;
|
||||
export default appImManager;
|
||||
|
@ -38,7 +38,7 @@ export type HistoryResult = {
|
||||
unreadSkip: boolean
|
||||
};
|
||||
|
||||
type Dialog = {
|
||||
export type Dialog = {
|
||||
_: 'dialog',
|
||||
top_message: number,
|
||||
read_inbox_max_id: number,
|
||||
@ -68,9 +68,14 @@ export class AppMessagesManager {
|
||||
[peerID: string]: HistoryStorage
|
||||
} = {};
|
||||
public dialogsStorage: {
|
||||
count: any,
|
||||
dialogs: Dialog[]
|
||||
} = {count: null, dialogs: []};
|
||||
count: number,
|
||||
dialogs: {
|
||||
[folderID: number]: Dialog[]
|
||||
}
|
||||
} = {
|
||||
count: null,
|
||||
dialogs: {}
|
||||
};
|
||||
public pendingByRandomID: {[randomID: string]: [number, number]} = {};
|
||||
public pendingByMessageID: any = {};
|
||||
public pendingAfterMsgs: any = {};
|
||||
@ -1137,37 +1142,12 @@ export class AppMessagesManager {
|
||||
};
|
||||
}
|
||||
|
||||
public getConversations(query?: string, offsetIndex?: number, limit = 20, folderID = -1) {
|
||||
//var curDialogStorage = this.dialogsStorage;
|
||||
//var isSearch = typeof(query) == 'string' && query.length;
|
||||
let curDialogStorage = this.dialogsStorage.dialogs;
|
||||
public getConversations(offsetIndex?: number, limit = 20, folderID = 0) {
|
||||
let curDialogStorage = this.dialogsStorage.dialogs[folderID] ?? (this.dialogsStorage.dialogs[folderID] = []);
|
||||
|
||||
if(folderID > 0) {
|
||||
curDialogStorage = curDialogStorage.filter(d => d.folder_id == folderID);
|
||||
} else {
|
||||
curDialogStorage = curDialogStorage.filter(d => d.folder_id != 1);
|
||||
}
|
||||
this.cachedResults.query = false;
|
||||
|
||||
/* if(isSearch) {
|
||||
if(!limit || this.cachedResults.query !== query) {
|
||||
this.cachedResults.query = query;
|
||||
|
||||
var results: any = SearchIndexManager.search(query, this.dialogsIndex);
|
||||
|
||||
this.cachedResults.dialogs = [];
|
||||
this.dialogsStorage.dialogs.forEach((dialog: any) => {
|
||||
if(results[dialog.peerID]) {
|
||||
this.cachedResults.dialogs.push(dialog);
|
||||
}
|
||||
})
|
||||
this.cachedResults.count = this.cachedResults.dialogs.length;
|
||||
}
|
||||
curDialogStorage = this.cachedResults;
|
||||
} else { */
|
||||
this.cachedResults.query = false;
|
||||
//}
|
||||
|
||||
var offset = 0;
|
||||
let offset = 0;
|
||||
if(offsetIndex > 0) {
|
||||
for(; offset < curDialogStorage.length; offset++) {
|
||||
if(offsetIndex > curDialogStorage[offset].index) {
|
||||
@ -1176,7 +1156,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
}
|
||||
|
||||
if(/* isSearch || */this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) {
|
||||
if(this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) {
|
||||
return Promise.resolve({
|
||||
dialogs: curDialogStorage.slice(offset, offset + limit),
|
||||
count: curDialogStorage.length
|
||||
@ -1184,17 +1164,11 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
return this.getTopMessages(limit, folderID).then(count => {
|
||||
let curDialogStorage = this.dialogsStorage.dialogs;
|
||||
|
||||
if(folderID > 0) {
|
||||
curDialogStorage = curDialogStorage.filter(d => d.folder_id == folderID);
|
||||
} else {
|
||||
curDialogStorage = curDialogStorage.filter(d => d.folder_id != 1);
|
||||
}
|
||||
let curDialogStorage = this.dialogsStorage.dialogs[folderID];
|
||||
|
||||
offset = 0;
|
||||
if(offsetIndex > 0) {
|
||||
for(offset = 0; offset < curDialogStorage.length; offset++) {
|
||||
for(; offset < curDialogStorage.length; offset++) {
|
||||
if(offsetIndex > curDialogStorage[offset].index) {
|
||||
break;
|
||||
}
|
||||
@ -1210,20 +1184,14 @@ export class AppMessagesManager {
|
||||
});
|
||||
}
|
||||
|
||||
public getTopMessages(limit: number, folderID = -1): Promise<number> {
|
||||
var dialogs = this.dialogsStorage.dialogs;
|
||||
public getTopMessages(limit: number, folderID: number): Promise<number> {
|
||||
var dialogs = this.dialogsStorage.dialogs[folderID];
|
||||
var offsetDate = 0;
|
||||
var offsetID = 0;
|
||||
var offsetPeerID = 0;
|
||||
var offsetIndex = 0;
|
||||
var flags = 0;
|
||||
|
||||
if(folderID > 0) {
|
||||
dialogs = dialogs.filter(d => d.folder_id == folderID);
|
||||
} else {
|
||||
dialogs = dialogs.filter(d => d.folder_id != 1);
|
||||
}
|
||||
|
||||
if(this.dialogsOffsetDate[folderID]) {
|
||||
offsetDate = this.dialogsOffsetDate[folderID] + serverTimeManager.serverTimeOffset;
|
||||
offsetIndex = this.dialogsOffsetDate[folderID] * 0x10000;
|
||||
@ -1260,13 +1228,10 @@ export class AppMessagesManager {
|
||||
|
||||
var maxSeenIdIncremented = offsetDate ? true : false;
|
||||
var hasPrepend = false;
|
||||
//dialogsResult.dialogs.reverse();
|
||||
let length = dialogsResult.dialogs.length;
|
||||
let noIDsDialogs: any = {};
|
||||
for(let i = length - 1; i >= 0; --i) {
|
||||
let dialog = dialogsResult.dialogs[i];
|
||||
//}
|
||||
//dialogsResult.dialogs.forEach((dialog: any) => {
|
||||
|
||||
this.saveConversation(dialog);
|
||||
if(offsetIndex && dialog.index > offsetIndex) {
|
||||
@ -1284,8 +1249,6 @@ export class AppMessagesManager {
|
||||
maxSeenIdIncremented = true;
|
||||
}
|
||||
}
|
||||
//});
|
||||
//dialogsResult.dialogs.reverse();
|
||||
|
||||
if(Object.keys(noIDsDialogs).length) {
|
||||
//setTimeout(() => { // test bad situation
|
||||
@ -1315,6 +1278,54 @@ export class AppMessagesManager {
|
||||
});
|
||||
}
|
||||
|
||||
public forwardMessages(peerID: number, mids: number[], options: Partial<{
|
||||
withMyScore: boolean
|
||||
}> = {}) {
|
||||
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
|
||||
mids = mids.sort((a, b) => a - b);
|
||||
|
||||
var flags = 0;
|
||||
if(options.withMyScore) {
|
||||
flags |= 256;
|
||||
}
|
||||
|
||||
let splitted = appMessagesIDsManager.splitMessageIDsByChannels(mids);
|
||||
let promises: any[] = [];
|
||||
|
||||
for(let channelID in splitted.msgIDs) {
|
||||
let msgIDs = splitted.msgIDs[channelID];
|
||||
let len = msgIDs.length;
|
||||
let randomIDs = [];
|
||||
for(let i = 0; i < len; i++) {
|
||||
randomIDs.push([nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]);
|
||||
}
|
||||
|
||||
let sentRequestOptions: any = {};
|
||||
if(this.pendingAfterMsgs[peerID]) {
|
||||
sentRequestOptions.afterMessageID = this.pendingAfterMsgs[peerID].messageID;
|
||||
}
|
||||
|
||||
let promise = apiManager.invokeApi('messages.forwardMessages', {
|
||||
flags: flags,
|
||||
from_peer: AppPeersManager.getInputPeerByID(-channelID),
|
||||
id: msgIDs,
|
||||
random_id: randomIDs,
|
||||
to_peer: AppPeersManager.getInputPeerByID(peerID)
|
||||
}, sentRequestOptions).then((updates) => {
|
||||
apiUpdatesManager.processUpdateMessage(updates);
|
||||
}, () => {}).then(() => {
|
||||
if(this.pendingAfterMsgs[peerID] === sentRequestOptions) {
|
||||
delete this.pendingAfterMsgs[peerID];
|
||||
}
|
||||
});
|
||||
|
||||
this.pendingAfterMsgs[peerID] = sentRequestOptions;
|
||||
promises.push(promise);
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
public generateDialogIndex(date?: any) {
|
||||
if(date === undefined) {
|
||||
date = tsNow(true) + serverTimeManager.serverTimeOffset;
|
||||
@ -1322,9 +1333,9 @@ export class AppMessagesManager {
|
||||
return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);
|
||||
}
|
||||
|
||||
public pushDialogToStorage(dialog: any, offsetDate?: number) {
|
||||
var dialogs = this.dialogsStorage.dialogs/* .filter(d => d.folder_id == dialog.folder_id) */;
|
||||
var pos = this.getDialogByPeerID(dialog.peerID)[1];
|
||||
public pushDialogToStorage(dialog: Dialog, offsetDate?: number) {
|
||||
let dialogs = this.dialogsStorage.dialogs[dialog.folder_id] ?? (this.dialogsStorage.dialogs[dialog.folder_id] = []);
|
||||
let pos = this.getDialogByPeerID(dialog.peerID)[1];
|
||||
if(pos !== undefined) {
|
||||
dialogs.splice(pos, 1);
|
||||
}
|
||||
@ -1339,15 +1350,14 @@ export class AppMessagesManager {
|
||||
this.dialogsOffsetDate[dialog.folder_id] = offsetDate;
|
||||
}
|
||||
|
||||
var index = dialog.index;
|
||||
var i;
|
||||
var len = dialogs.length;
|
||||
let index = dialog.index;
|
||||
let len = dialogs.length;
|
||||
if(!len || index < dialogs[len - 1].index) {
|
||||
dialogs.push(dialog);
|
||||
} else if(index >= dialogs[0].index) {
|
||||
dialogs.unshift(dialog);
|
||||
} else {
|
||||
for(i = 0; i < len; i++) {
|
||||
for(let i = 0; i < len; i++) {
|
||||
if(index > dialogs[i].index) {
|
||||
dialogs.splice(i, 0, dialog);
|
||||
break;
|
||||
@ -1377,22 +1387,10 @@ export class AppMessagesManager {
|
||||
|
||||
public getDialogByPeerID(peerID: number): [Dialog, number] | [] {
|
||||
let dialogs = this.dialogsStorage.dialogs;
|
||||
let byFolders: {[id: number]: number} = {};
|
||||
for(let i = 0, length = dialogs.length; i < length; i++) {
|
||||
let dialog = dialogs[i];
|
||||
if(!byFolders[dialog.folder_id]) byFolders[dialog.folder_id] = 0;
|
||||
byFolders[dialog.folder_id]++;
|
||||
|
||||
if(dialog.peerID == peerID) {
|
||||
//return [dialog, i];
|
||||
let sum = 0;
|
||||
for(let id in byFolders) {
|
||||
if(+id != dialog.folder_id) {
|
||||
sum += byFolders[id];
|
||||
}
|
||||
}
|
||||
|
||||
return [dialog, i - sum];
|
||||
for(let folderID in dialogs) {
|
||||
let index = dialogs[folderID].findIndex(dialog => dialog.peerID == peerID);
|
||||
if(index !== -1) {
|
||||
return [dialogs[folderID][index], index];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1627,7 +1625,7 @@ export class AppMessagesManager {
|
||||
})
|
||||
}
|
||||
|
||||
public migrateChecks(migrateFrom: any, migrateTo: any) {
|
||||
public migrateChecks(migrateFrom: number, migrateTo: number) {
|
||||
if(!this.migratedFromTo[migrateFrom] &&
|
||||
!this.migratedToFrom[migrateTo] &&
|
||||
appChatsManager.hasChat(-migrateTo)) {
|
||||
@ -1641,9 +1639,10 @@ export class AppMessagesManager {
|
||||
setTimeout(() => {
|
||||
var foundDialog = this.getDialogByPeerID(migrateFrom);
|
||||
if(foundDialog.length) {
|
||||
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
|
||||
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||||
$rootScope.$broadcast('dialog_drop', {peerID: migrateFrom});
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('dialog_migrate', {migrateFrom: migrateFrom, migrateTo: migrateTo});
|
||||
}, 100);
|
||||
}
|
||||
@ -1734,7 +1733,7 @@ export class AppMessagesManager {
|
||||
} else {
|
||||
var foundDialog = this.getDialogByPeerID(peerID);
|
||||
if(foundDialog.length) {
|
||||
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
|
||||
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||||
$rootScope.$broadcast('dialog_drop', {peerID: peerID});
|
||||
}
|
||||
}
|
||||
@ -1853,256 +1852,8 @@ export class AppMessagesManager {
|
||||
if(channelID && dialog.pts) {
|
||||
apiUpdatesManager.addChannelState(channelID, dialog.pts);
|
||||
}
|
||||
|
||||
/*if(Config.Modes.packed && !channelID && dialog.unread_count > 0 &&
|
||||
this.maxSeenID && dialog.top_message > this.maxSeenID &&
|
||||
message.pFlags.unread && !message.pFlags.out) {
|
||||
var notifyPeer = message.flags & 16 ? message.from_id : peerID
|
||||
NotificationsManager.getPeerMuted(notifyPeer).then((muted: any) => {
|
||||
if(!muted) {
|
||||
this.notifyAboutMessage(message);
|
||||
}
|
||||
});
|
||||
}*/ // WARNING
|
||||
}
|
||||
|
||||
/*public handleNotifications() {
|
||||
clearTimeout(this.notificationsHandlePromise);
|
||||
this.notificationsHandlePromise = 0;
|
||||
|
||||
var timeout = $rootScope.idle.isIDLE /* && StatusManager.isOtherDeviceActive() * ? 30000 : 1000;
|
||||
Object.keys(this.notificationsToHandle).forEach((key: any) => {
|
||||
let notifyPeerToHandle = this.notificationsToHandle[key];
|
||||
notifyPeerToHandle.isMutedPromise.then((muted: boolean) => {
|
||||
var topMessage = notifyPeerToHandle.top_message
|
||||
if(muted ||
|
||||
!topMessage.pFlags.unread) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if(topMessage.pFlags.unread) {
|
||||
this.notifyAboutMessage(topMessage, {
|
||||
fwd_count: notifyPeerToHandle.fwd_count
|
||||
});
|
||||
}
|
||||
}, timeout);
|
||||
});
|
||||
});
|
||||
|
||||
this.notificationsToHandle = {};
|
||||
}*/
|
||||
|
||||
/*public notifyAboutMessage(message: any, options: any = {}) {
|
||||
var peerID = this.getMessagePeer(message);
|
||||
var peerString: string;
|
||||
var notification: any = {};
|
||||
var notificationMessage = '',
|
||||
notificationPhoto;
|
||||
|
||||
var notifySettings: any = {}; //NotificationsManager.getNotifySettings(); // warning
|
||||
|
||||
if(message.fwdFromID && options.fwd_count) {
|
||||
notificationMessage = options.fwd_count;// this.fwdMessagesPluralize(options.fwd_count); // warning
|
||||
} else if(message.message) {
|
||||
if(notifySettings.nopreview) {
|
||||
notificationMessage = 'conversation_message_sent';
|
||||
} else {
|
||||
notificationMessage = RichTextProcessor.wrapPlainText(message.message);
|
||||
}
|
||||
} else if(message.media) {
|
||||
var captionEmoji = '';
|
||||
switch (message.media._) {
|
||||
case 'messageMediaPhoto':
|
||||
notificationMessage = _('conversation_media_photo_raw');
|
||||
captionEmoji = 'рџ–ј';
|
||||
break
|
||||
case 'messageMediaDocument':
|
||||
switch (message.media.document.type) {
|
||||
case 'gif':
|
||||
notificationMessage = _('conversation_media_gif_raw');
|
||||
captionEmoji = 'рџЋ¬'
|
||||
break
|
||||
case 'sticker':
|
||||
notificationMessage = _('conversation_media_sticker');
|
||||
var stickerEmoji = message.media.document.stickerEmojiRaw;
|
||||
if(stickerEmoji !== undefined) {
|
||||
notificationMessage = RichTextProcessor.wrapPlainText(stickerEmoji) + ' ' + notificationMessage;
|
||||
}
|
||||
break;
|
||||
case 'video':
|
||||
notificationMessage = _('conversation_media_video_raw');
|
||||
captionEmoji = 'рџ“№';
|
||||
break;
|
||||
case 'round':
|
||||
notificationMessage = _('conversation_media_round_raw');
|
||||
captionEmoji = 'рџ“№';
|
||||
break;
|
||||
case 'voice':
|
||||
case 'audio':
|
||||
notificationMessage = _('conversation_media_audio_raw');
|
||||
break;
|
||||
default:
|
||||
if(message.media.document.file_name) {
|
||||
notificationMessage = RichTextProcessor.wrapPlainText('рџ“Ћ ' + message.media.document.file_name);
|
||||
} else {
|
||||
notificationMessage = _('conversation_media_document_raw');
|
||||
captionEmoji = 'рџ“Ћ';
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'messageMediaGeo':
|
||||
case 'messageMediaVenue':
|
||||
notificationMessage = _('conversation_media_location_raw');
|
||||
captionEmoji = 'рџ“Ќ';
|
||||
break;
|
||||
case 'messageMediaContact':
|
||||
notificationMessage = _('conversation_media_contact_raw');
|
||||
break;
|
||||
case 'messageMediaGame':
|
||||
notificationMessage = RichTextProcessor.wrapPlainText('рџЋ® ' + message.media.game.title);
|
||||
break;
|
||||
case 'messageMediaUnsupported':
|
||||
notificationMessage = _('conversation_media_unsupported_raw');
|
||||
break;
|
||||
default:
|
||||
notificationMessage = _('conversation_media_attachment_raw');
|
||||
break;
|
||||
}
|
||||
|
||||
if(captionEmoji != '' &&
|
||||
message.media.caption) {
|
||||
notificationMessage = RichTextProcessor.wrapPlainText(captionEmoji + ' ' + message.media.caption);
|
||||
}
|
||||
} else if(message._ == 'messageService') {
|
||||
switch(message.action._) {
|
||||
case 'messageActionChatCreate':
|
||||
notificationMessage = _('conversation_group_created_raw');
|
||||
break
|
||||
case 'messageActionChatEditTitle':
|
||||
notificationMessage = _('conversation_group_renamed_raw');
|
||||
break
|
||||
case 'messageActionChatEditPhoto':
|
||||
notificationMessage = _('conversation_group_photo_updated_raw');
|
||||
break
|
||||
case 'messageActionChatDeletePhoto':
|
||||
notificationMessage = _('conversation_group_photo_removed_raw');
|
||||
break
|
||||
case 'messageActionChatAddUser':
|
||||
case 'messageActionChatAddUsers':
|
||||
notificationMessage = _('conversation_invited_user_message_raw');
|
||||
break
|
||||
case 'messageActionChatReturn':
|
||||
notificationMessage = _('conversation_returned_to_group_raw');
|
||||
break
|
||||
case 'messageActionChatJoined':
|
||||
notificationMessage = _('conversation_joined_group_raw');
|
||||
break
|
||||
case 'messageActionChatDeleteUser':
|
||||
notificationMessage = _('conversation_kicked_user_message_raw');
|
||||
break
|
||||
case 'messageActionChatLeave':
|
||||
notificationMessage = _('conversation_left_group_raw');
|
||||
break
|
||||
case 'messageActionChatJoinedByLink':
|
||||
notificationMessage = _('conversation_joined_by_link_raw');
|
||||
break
|
||||
case 'messageActionChannelCreate':
|
||||
notificationMessage = _('conversation_created_channel_raw');
|
||||
break
|
||||
case 'messageActionChannelEditTitle':
|
||||
notificationMessage = _('conversation_changed_channel_name_raw');
|
||||
break
|
||||
case 'messageActionChannelEditPhoto':
|
||||
notificationMessage = _('conversation_changed_channel_photo_raw')
|
||||
break
|
||||
case 'messageActionChannelDeletePhoto':
|
||||
notificationMessage = _('conversation_removed_channel_photo_raw')
|
||||
break
|
||||
case 'messageActionPinMessage':
|
||||
notificationMessage = _('conversation_pinned_message_raw')
|
||||
break
|
||||
case 'messageActionGameScore':
|
||||
notificationMessage = message.action.score;//this.gameScorePluralize(message.action.score); // warning
|
||||
break
|
||||
|
||||
case 'messageActionPhoneCall':
|
||||
switch(message.action.type) {
|
||||
case 'out_missed':
|
||||
notificationMessage = _('message_service_phonecall_canceled_raw')
|
||||
break
|
||||
case 'in_missed':
|
||||
notificationMessage = _('message_service_phonecall_missed_raw')
|
||||
break
|
||||
case 'out_ok':
|
||||
notificationMessage = _('message_service_phonecall_outgoing_raw')
|
||||
break
|
||||
case 'in_ok':
|
||||
notificationMessage = _('message_service_phonecall_incoming_raw')
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if(peerID > 0) {
|
||||
var fromUser = appUsersManager.getUser(message.from_id);
|
||||
var fromPhoto = appUsersManager.getUserPhoto(message.from_id);
|
||||
|
||||
notification.title = (fromUser.first_name || '') +
|
||||
(fromUser.first_name && fromUser.last_name ? ' ' : '') +
|
||||
(fromUser.last_name || '')
|
||||
if(!notification.title) {
|
||||
notification.title = fromUser.phone || _('conversation_unknown_user_raw')
|
||||
}
|
||||
|
||||
notificationPhoto = fromPhoto
|
||||
|
||||
peerString = appUsersManager.getUserString(peerID)
|
||||
} else {
|
||||
notification.title = appChatsManager.getChat(-peerID).title || _('conversation_unknown_chat_raw')
|
||||
|
||||
if(message.from_id > 0) {
|
||||
var fromUser = appUsersManager.getUser(message.from_id)
|
||||
notification.title = (fromUser.first_name || fromUser.last_name || _('conversation_unknown_user_raw')) +
|
||||
' @ ' +
|
||||
notification.title
|
||||
}
|
||||
|
||||
notificationPhoto = appChatsManager.getChatPhoto(-peerID)
|
||||
|
||||
peerString = appChatsManager.getChatString(-peerID)
|
||||
}
|
||||
|
||||
notification.title = RichTextProcessor.wrapPlainText(notification.title)
|
||||
|
||||
notification.onclick = function () {
|
||||
$rootScope.$broadcast('history_focus', {
|
||||
peerString: peerString,
|
||||
messageID: message.flags & 16 ? message.mid : 0
|
||||
})
|
||||
}
|
||||
|
||||
notification.message = notificationMessage
|
||||
notification.key = 'msg' + message.mid
|
||||
notification.tag = peerString
|
||||
notification.silent = message.pFlags.silent || false
|
||||
|
||||
if(notificationPhoto.location && !notificationPhoto.location.empty) {
|
||||
apiFileManager.downloadSmallFile(notificationPhoto.location/* , notificationPhoto.size *)
|
||||
.then((blob) => {
|
||||
if(message.pFlags.unread) {
|
||||
notification.image = blob
|
||||
// NotificationsManager.notify(notification) // warning
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// NotificationsManager.notify(notification) // warning
|
||||
}
|
||||
}*/
|
||||
|
||||
public mergeReplyKeyboard(historyStorage: any, message: any) {
|
||||
// console.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup)
|
||||
if(!message.reply_markup &&
|
||||
@ -2516,15 +2267,6 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
return false;
|
||||
/* if(foundDialog) {
|
||||
// console.log('done read history', peerID)
|
||||
foundDialog.unread_count = 0
|
||||
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: 0})
|
||||
$rootScope.$broadcast('messages_read')
|
||||
if(historyStorage && historyStorage.history.length) {
|
||||
foundDialog.read_inbox_max_id = historyStorage.history[0]
|
||||
}
|
||||
} */
|
||||
}).finally(() => {
|
||||
delete historyStorage.readPromise;
|
||||
});
|
||||
@ -2593,12 +2335,12 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
public handleUpdate(update: any) {
|
||||
console.log('AMM: handleUpdate:', update._);
|
||||
//console.log('AMM: handleUpdate:', update._);
|
||||
switch(update._) {
|
||||
case 'updateMessageID': {
|
||||
var randomID = update.random_id;
|
||||
var pendingData = this.pendingByRandomID[randomID];
|
||||
console.log('AMM updateMessageID:', update, pendingData);
|
||||
//console.log('AMM updateMessageID:', update, pendingData);
|
||||
if(pendingData) {
|
||||
var peerID: number = pendingData[0];
|
||||
var tempID = pendingData[1];
|
||||
@ -2720,36 +2462,10 @@ export class AppMessagesManager {
|
||||
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
|
||||
}
|
||||
|
||||
/*if(inboxUnread &&
|
||||
($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE)) {
|
||||
var notifyPeer = message.flags & 16 ? message.from_id : peerID;
|
||||
var notifyPeerToHandle = this.notificationsToHandle[notifyPeer];
|
||||
if(notifyPeerToHandle === undefined) {
|
||||
notifyPeerToHandle = this.notificationsToHandle[notifyPeer] = {
|
||||
isMutedPromise: Promise.resolve()/* NotificationsManager.getPeerMuted(notifyPeer), // WARNING
|
||||
fwd_count: 0,
|
||||
from_id: 0
|
||||
};
|
||||
}
|
||||
|
||||
if(notifyPeerToHandle.from_id != message.from_id) {
|
||||
notifyPeerToHandle.from_id = message.from_id;
|
||||
notifyPeerToHandle.fwd_count = 0;
|
||||
}
|
||||
if(message.fwdFromID) {
|
||||
notifyPeerToHandle.fwd_count++;
|
||||
}
|
||||
|
||||
notifyPeerToHandle.top_message = message;
|
||||
|
||||
if(!this.notificationsHandlePromise) {
|
||||
this.notificationsHandlePromise = window.setTimeout(this.handleNotifications.bind(this), 1000);
|
||||
}
|
||||
} */
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateDialogPinned': {
|
||||
/* case 'updateDialogPinned': {
|
||||
var peerID = AppPeersManager.getPeerID(update.peer);
|
||||
var foundDialog = this.getDialogByPeerID(peerID);
|
||||
|
||||
@ -2826,7 +2542,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
})
|
||||
break;
|
||||
}
|
||||
} */
|
||||
|
||||
case 'updateEditMessage':
|
||||
case 'updateEditChannelMessage': {
|
||||
@ -3092,37 +2808,41 @@ export class AppMessagesManager {
|
||||
delete this.historiesStorage[peerID];
|
||||
$rootScope.$broadcast('history_forbidden', peerID);
|
||||
}
|
||||
|
||||
if(hasDialog != needDialog) {
|
||||
if(needDialog) {
|
||||
this.reloadConversation(-channelID);
|
||||
} else {
|
||||
if(foundDialog[0]) {
|
||||
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
|
||||
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||||
$rootScope.$broadcast('dialog_drop', {peerID: peerID});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateChannelReload': {
|
||||
var channelID: number = update.channel_id;
|
||||
var peerID = -channelID;
|
||||
var foundDialog = this.getDialogByPeerID(peerID);
|
||||
let channelID: number = update.channel_id;
|
||||
let peerID = -channelID;
|
||||
let foundDialog = this.getDialogByPeerID(peerID);
|
||||
if(foundDialog[0]) {
|
||||
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
|
||||
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||||
}
|
||||
|
||||
delete this.historiesStorage[peerID];
|
||||
this.reloadConversation(-channelID).then(() => {
|
||||
$rootScope.$broadcast('history_reload', peerID);
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateChannelMessageViews': {
|
||||
var views = update.views;
|
||||
var mid = appMessagesIDsManager.getFullMessageID(update.id, update.channel_id);
|
||||
var message = this.getMessage(mid);
|
||||
let views = update.views;
|
||||
let mid = appMessagesIDsManager.getFullMessageID(update.id, update.channel_id);
|
||||
let message = this.getMessage(mid);
|
||||
if(message && message.views && message.views < views) {
|
||||
message.views = views;
|
||||
$rootScope.$broadcast('message_views', {
|
||||
@ -3567,57 +3287,6 @@ export class AppMessagesManager {
|
||||
});
|
||||
}
|
||||
|
||||
/* public wrapForDialog(msgID: number, dialog?: any) {
|
||||
var useCache = msgID && dialog !== undefined;
|
||||
var unreadCount = dialog && dialog.unread_count;
|
||||
|
||||
if(useCache && this.messagesForDialogs[msgID] !== undefined) {
|
||||
delete this.messagesForDialogs[msgID].typing;
|
||||
this.messagesForDialogs[msgID].unreadCount = unreadCount;
|
||||
return this.messagesForDialogs[msgID];
|
||||
}
|
||||
|
||||
var message = copy(this.messagesStorage[msgID]);
|
||||
|
||||
if(!message || !message.to_id) {
|
||||
if(dialog && dialog.peerID) {
|
||||
message = {
|
||||
_: 'message',
|
||||
to_id: AppPeersManager.getOutputPeer(dialog.peerID),
|
||||
deleted: true,
|
||||
date: tsNow(true),
|
||||
pFlags: {out: true}
|
||||
}
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
message.peerID = this.getMessagePeer(message);
|
||||
message.peerData = AppPeersManager.getPeer(message.peerID);
|
||||
message.peerString = AppPeersManager.getPeerString(message.peerID);
|
||||
message.unreadCount = unreadCount;
|
||||
message.index = dialog && dialog.index || (message.date * 0x10000);
|
||||
message.pinned = dialog && dialog.pFlags.pinned || false;
|
||||
|
||||
if(message._ == 'messageService' && message.action.user_id) {
|
||||
message.action.user = appUsersManager.getUser(message.action.user_id);
|
||||
}
|
||||
|
||||
if(message.message && message.message.length) {
|
||||
message.richMessage = RichTextProcessor.wrapRichText(message.message.substr(0, 128), {noLinks: true, noLinebreaks: true});
|
||||
}
|
||||
|
||||
message.dateText = message.date; //dateOrTimeFilter(message.date); // warning
|
||||
|
||||
if(useCache) {
|
||||
message.draft = '';//DraftsManager.getServerDraft(message.peerID); // warning
|
||||
this.messagesForDialogs[msgID] = message;
|
||||
}
|
||||
|
||||
return message;
|
||||
} */
|
||||
|
||||
public fetchSingleMessages() {
|
||||
if(this.fetchSingleMessagesPromise) {
|
||||
return this.fetchSingleMessagesPromise;
|
||||
|
@ -37,7 +37,7 @@ const AppPeersManager = {
|
||||
return false;
|
||||
},
|
||||
|
||||
getPeerTitle: (peerID: number | any, plainText = false) => {
|
||||
getPeerTitle: (peerID: number | any, plainText = false, onlyFirstName = false) => {
|
||||
let peer: any = {};
|
||||
if(!isObject(peerID)) {
|
||||
peer = AppPeersManager.getPeer(peerID);
|
||||
@ -53,6 +53,10 @@ const AppPeersManager = {
|
||||
} else {
|
||||
title = peer.title;
|
||||
}
|
||||
|
||||
if(onlyFirstName) {
|
||||
title = title.split(' ')[0];
|
||||
}
|
||||
|
||||
return plainText ? title : RichTextProcessor.wrapEmojiText(title);
|
||||
},
|
||||
|
@ -27,6 +27,12 @@ export class AppPhotosManager {
|
||||
private photos: {
|
||||
[id: string]: MTPhoto
|
||||
} = {};
|
||||
private documentThumbsCache: {
|
||||
[docID: string]: {
|
||||
downloaded: number,
|
||||
url: string
|
||||
}
|
||||
} = {};
|
||||
public windowW = 0;
|
||||
public windowH = 0;
|
||||
|
||||
@ -243,7 +249,7 @@ export class AppPhotosManager {
|
||||
element.setAttributeNS(null, 'width', '' + w);
|
||||
element.setAttributeNS(null, 'height', '' + h);
|
||||
|
||||
console.log('set dimensions to svg element:', element, w, h);
|
||||
//console.log('set dimensions to svg element:', element, w, h);
|
||||
|
||||
if(element.firstElementChild) {
|
||||
let imageSvg = element.firstElementChild as SVGImageElement;
|
||||
@ -268,7 +274,10 @@ export class AppPhotosManager {
|
||||
photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
|
||||
}
|
||||
|
||||
if(photo.downloaded >= photoSize.size && photo.url) {
|
||||
let isDocument = photo._ == 'document';
|
||||
let cacheContext = isDocument ? (this.documentThumbsCache[photo.id] ?? (this.documentThumbsCache[photo.id] = {downloaded: -1, url: ''})) : photo;
|
||||
|
||||
if(cacheContext.downloaded >= photoSize.size && cacheContext.url) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@ -280,7 +289,7 @@ export class AppPhotosManager {
|
||||
// maybe it's a thumb
|
||||
let isPhoto = photoSize.size && photo.access_hash && photo.file_reference;
|
||||
let location = isPhoto ? {
|
||||
_: photo._ == 'document' ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
||||
_: isDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
||||
id: photo.id,
|
||||
access_hash: photo.access_hash,
|
||||
file_reference: photo.file_reference,
|
||||
@ -296,20 +305,21 @@ export class AppPhotosManager {
|
||||
promise = apiFileManager.downloadSmallFile(location);
|
||||
}
|
||||
|
||||
if(typeof(photoID) === 'string') {
|
||||
let photo = this.photos[photoID];
|
||||
promise.then(blob => {
|
||||
if(!photo.downloaded || photo.downloaded < blob.size) {
|
||||
photo.downloaded = blob.size;
|
||||
photo.url = URL.createObjectURL(blob);
|
||||
promise.then(blob => {
|
||||
if(!cacheContext.downloaded || cacheContext.downloaded < blob.size) {
|
||||
cacheContext.downloaded = blob.size;
|
||||
cacheContext.url = URL.createObjectURL(blob);
|
||||
|
||||
console.log('wrote photo:', photo, photoSize, blob);
|
||||
}
|
||||
});
|
||||
}
|
||||
//console.log('wrote photo:', photo, photoSize, cacheContext, blob);
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
public getDocumentCachedThumb(docID: string) {
|
||||
return this.documentThumbsCache[docID];
|
||||
}
|
||||
|
||||
public getPhoto(photoID: any): MTPhoto {
|
||||
return isObject(photoID) ? photoID : this.photos[photoID];
|
||||
|
@ -5,6 +5,16 @@ import appImManager from "./appImManager";
|
||||
//import apiManager from '../mtproto/apiManager';
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
import AppSearch, { SearchGroup } from "../../components/appSearch";
|
||||
import { horizontalMenu } from "../../components/misc";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
import Scrollable from "../../components/scrollable_new";
|
||||
import appPhotosManager from "./appPhotosManager";
|
||||
import { appPeersManager } from "../services";
|
||||
|
||||
const SLIDERITEMSIDS = {
|
||||
archived: 1,
|
||||
contacts: 2
|
||||
};
|
||||
|
||||
class AppSidebarLeft {
|
||||
private sidebarEl = document.getElementById('column-left') as HTMLDivElement;
|
||||
@ -16,18 +26,36 @@ class AppSidebarLeft {
|
||||
private menuEl = this.toolsBtn.querySelector('.btn-menu');
|
||||
private savedBtn = this.menuEl.querySelector('.menu-saved');
|
||||
private archivedBtn = this.menuEl.querySelector('.menu-archive');
|
||||
private contactsBtn = this.menuEl.querySelector('.menu-contacts');
|
||||
private logOutBtn = this.menuEl.querySelector('.menu-logout');
|
||||
public archivedCount = this.archivedBtn.querySelector('.archived-count') as HTMLSpanElement;
|
||||
|
||||
//private log = logger('SL');
|
||||
|
||||
private globalSearch = new AppSearch(this.searchContainer, this.searchInput, {
|
||||
private searchGroups = {
|
||||
contacts: new SearchGroup('Contacts and Chats', 'contacts'),
|
||||
globalContacts: new SearchGroup('Global Search', 'contacts'),
|
||||
messages: new SearchGroup('Global Search', 'messages')
|
||||
});
|
||||
messages: new SearchGroup('Global Search', 'messages'),
|
||||
people: new SearchGroup('People', 'contacts', false, 'search-group-people'),
|
||||
recent: new SearchGroup('Recent', 'contacts', false, 'search-group-recent')
|
||||
};
|
||||
private globalSearch = new AppSearch(this.searchContainer, this.searchInput, this.searchGroups);
|
||||
|
||||
private _selectTab: (id: number) => void;
|
||||
private historyTabIDs: number[] = [];
|
||||
|
||||
private contactsList: HTMLUListElement;
|
||||
private contactsScrollable: Scrollable;
|
||||
private contactsPromise: Promise<void>;
|
||||
private contactsInput: HTMLInputElement;
|
||||
|
||||
constructor() {
|
||||
let peopleContainer = document.createElement('div');
|
||||
peopleContainer.classList.add('search-group-scrollable');
|
||||
peopleContainer.append(this.searchGroups.people.list);
|
||||
this.searchGroups.people.container.append(peopleContainer);
|
||||
let peopleScrollable = new Scrollable(peopleContainer, 'x');
|
||||
|
||||
this.savedBtn.addEventListener('click', (e) => {
|
||||
///////this.log('savedbtn click');
|
||||
setTimeout(() => { // menu doesn't close if no timeout (lol)
|
||||
@ -37,11 +65,12 @@ class AppSidebarLeft {
|
||||
});
|
||||
|
||||
this.archivedBtn.addEventListener('click', (e) => {
|
||||
appDialogsManager.chatsArchivedContainer.classList.add('active');
|
||||
this.toolsBtn.classList.remove('active');
|
||||
this.backBtn.classList.add('active');
|
||||
//this.toolsBtn.classList.remove('tgico-menu', 'btn-menu-toggle');
|
||||
//this.toolsBtn.classList.add('tgico-back');
|
||||
this.selectTab(SLIDERITEMSIDS.archived);
|
||||
});
|
||||
|
||||
this.contactsBtn.addEventListener('click', (e) => {
|
||||
this.openContacts();
|
||||
this.selectTab(SLIDERITEMSIDS.contacts);
|
||||
});
|
||||
|
||||
this.logOutBtn.addEventListener('click', (e) => {
|
||||
@ -51,6 +80,8 @@ class AppSidebarLeft {
|
||||
this.searchInput.addEventListener('focus', (e) => {
|
||||
this.toolsBtn.classList.remove('active');
|
||||
this.backBtn.classList.add('active');
|
||||
this.searchContainer.classList.remove('hide');
|
||||
void this.searchContainer.offsetWidth; // reflow
|
||||
this.searchContainer.classList.add('active');
|
||||
|
||||
/* if(!this.globalSearch.searchInput.value) {
|
||||
@ -59,11 +90,10 @@ class AppSidebarLeft {
|
||||
}
|
||||
} */
|
||||
|
||||
this.searchInput.addEventListener('blur', (e) => {
|
||||
false && this.searchInput.addEventListener('blur', (e) => {
|
||||
if(!this.searchInput.value) {
|
||||
this.toolsBtn.classList.add('active');
|
||||
this.backBtn.classList.remove('active');
|
||||
this.searchContainer.classList.remove('active');
|
||||
this.backBtn.click();
|
||||
}
|
||||
|
||||
@ -78,16 +108,129 @@ class AppSidebarLeft {
|
||||
this.toolsBtn.classList.add('active');
|
||||
this.backBtn.classList.remove('active');
|
||||
this.searchContainer.classList.remove('active');
|
||||
this.globalSearch.reset();
|
||||
|
||||
setTimeout(() => {
|
||||
this.searchContainer.classList.add('hide');
|
||||
this.globalSearch.reset();
|
||||
|
||||
this.searchGroups.people.setActive();
|
||||
//this.searchGroups.recent.setActive();
|
||||
}, 150);
|
||||
});
|
||||
|
||||
$rootScope.$on('dialogs_archived_unread', (e: CustomEvent) => {
|
||||
this.archivedCount.innerText = '' + e.detail.count;
|
||||
});
|
||||
|
||||
/* appUsersManager.getTopPeers().then(categories => {
|
||||
this.log('got top categories:', categories);
|
||||
}); */
|
||||
this._selectTab = horizontalMenu(null, this.sidebarEl.querySelector('.sidebar-slider') as HTMLDivElement, null, null, 420);
|
||||
this._selectTab(0);
|
||||
|
||||
Array.from(this.sidebarEl.querySelectorAll('.sidebar-close-button') as any as HTMLElement[]).forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
console.log('sidebar-close-button click:', this.historyTabIDs);
|
||||
let closingID = this.historyTabIDs.pop(); // pop current
|
||||
|
||||
// need to clear, and left 1 page for smooth slide
|
||||
if(closingID == SLIDERITEMSIDS.contacts) {
|
||||
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
|
||||
(Array.from(this.contactsList.children) as HTMLElement[]).slice(pageCount).forEach(el => el.remove());
|
||||
setTimeout(() => {
|
||||
this.contactsList.innerHTML = '';
|
||||
}, 420);
|
||||
}
|
||||
|
||||
this._selectTab(this.historyTabIDs.pop() || 0);
|
||||
});
|
||||
});
|
||||
|
||||
appUsersManager.getTopPeers().then(categories => {
|
||||
console.log('got top categories:', categories);
|
||||
|
||||
let category = categories[0];
|
||||
if(!category || !category.peers) {
|
||||
return;
|
||||
}
|
||||
|
||||
category.peers.forEach((topPeer: {
|
||||
_: 'topPeer',
|
||||
peer: any,
|
||||
rating: number
|
||||
}) => {
|
||||
let peerID = appPeersManager.getPeerID(topPeer.peer);
|
||||
let {dialog, dom} = appDialogsManager.addDialog(peerID, this.searchGroups.people.list, false, true, true);
|
||||
|
||||
this.searchGroups.people.setActive();
|
||||
});
|
||||
});
|
||||
|
||||
let contactsContainer = this.sidebarEl.querySelector('#contacts-container');
|
||||
this.contactsInput = contactsContainer.querySelector('#contacts-search');
|
||||
this.contactsList = contactsContainer.querySelector('#contacts') as HTMLUListElement;
|
||||
appDialogsManager.setListClickListener(this.contactsList);
|
||||
this.contactsScrollable = new Scrollable(this.contactsList.parentElement);
|
||||
|
||||
let prevValue = '';
|
||||
this.contactsInput.addEventListener('input', () => {
|
||||
let value = this.contactsInput.value;
|
||||
if(prevValue != value) {
|
||||
this.contactsList.innerHTML = '';
|
||||
this.openContacts(prevValue = value);
|
||||
}
|
||||
});
|
||||
|
||||
// preload contacts
|
||||
appUsersManager.getContacts();
|
||||
}
|
||||
|
||||
public openContacts(query?: string) {
|
||||
if(this.contactsPromise) return this.contactsPromise;
|
||||
this.contactsScrollable.onScrolledBottom = null;
|
||||
|
||||
this.contactsPromise = appUsersManager.getContacts(query).then(contacts => {
|
||||
this.contactsPromise = null;
|
||||
|
||||
if(this.historyTabIDs[this.historyTabIDs.length - 1] != SLIDERITEMSIDS.contacts) {
|
||||
console.warn('user closed contacts before it\'s loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
let sorted = contacts
|
||||
.map(userID => {
|
||||
let user = appUsersManager.getUser(userID);
|
||||
let status = appUsersManager.getUserStatusForSort(user.status);
|
||||
|
||||
return {user, status};
|
||||
})
|
||||
.sort((a, b) => b.status - a.status);
|
||||
|
||||
let renderPage = () => {
|
||||
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
|
||||
let arr = sorted.splice(0, pageCount);
|
||||
|
||||
arr.forEach(({user}) => {
|
||||
let {dialog, dom} = appDialogsManager.addDialog(user.id, this.contactsList, false);
|
||||
|
||||
let status = appUsersManager.getUserStatusString(user.id);
|
||||
dom.lastMessageSpan.innerHTML = status == 'online' ? `<i>${status}</i>` : status;
|
||||
});
|
||||
|
||||
if(!sorted.length) renderPage = undefined;
|
||||
};
|
||||
|
||||
renderPage();
|
||||
this.contactsScrollable.onScrolledBottom = () => {
|
||||
if(renderPage) {
|
||||
renderPage();
|
||||
} else {
|
||||
this.contactsScrollable.onScrolledBottom = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public selectTab(id: number) {
|
||||
this.historyTabIDs.push(id);
|
||||
this._selectTab(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,12 +59,6 @@ class AppSidebarRight {
|
||||
private sharedMediaSelected: HTMLDivElement = null;
|
||||
|
||||
private lazyLoadQueueSidebar = new LazyLoadQueue(5);
|
||||
/* public minMediaID: {
|
||||
[type: string]: number
|
||||
} = {}; */
|
||||
public cleared: {
|
||||
[type: string]: boolean
|
||||
} = {};
|
||||
|
||||
public historiesStorage: {
|
||||
[peerID: number]: {
|
||||
@ -121,7 +115,7 @@ class AppSidebarRight {
|
||||
this.scroll.scrollTop -= this.profileTabs.offsetTop;
|
||||
}
|
||||
|
||||
this.log('setVirtualContainer', id, this.sharedMediaSelected);
|
||||
this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount);
|
||||
this.scroll.setVirtualContainer(this.sharedMediaSelected);
|
||||
|
||||
if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix
|
||||
@ -132,7 +126,10 @@ class AppSidebarRight {
|
||||
this.prevTabID = id;
|
||||
|
||||
this.scroll.onScroll();
|
||||
}, this.onSidebarScroll.bind(this));
|
||||
}, () => {
|
||||
this.onSidebarScroll.bind(this);
|
||||
this.scroll.onScroll();
|
||||
});
|
||||
|
||||
let sidebarCloseBtn = this.sidebarEl.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
||||
sidebarCloseBtn.addEventListener('click', () => {
|
||||
@ -212,24 +209,18 @@ class AppSidebarRight {
|
||||
|
||||
this.sidebarEl.classList.toggle('active');
|
||||
}
|
||||
|
||||
public performSearchResult(ids: number[], type: string) {
|
||||
let peerID = this.peerID;
|
||||
|
||||
let sharedMediaDiv: HTMLDivElement;
|
||||
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 elemsToAppend: HTMLElement[] = [];
|
||||
|
||||
// https://core.telegram.org/type/MessagesFilter
|
||||
|
||||
let filtered: any[] = [];
|
||||
|
||||
switch(type) {
|
||||
case 'inputMessagesFilterPhotoVideo': {
|
||||
sharedMediaDiv = this.sharedMedia.contentMedia;
|
||||
|
||||
for(let message of messages) {
|
||||
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
|
||||
if(!media) {
|
||||
@ -241,14 +232,85 @@ class AppSidebarRight {
|
||||
//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 performSearchResult(messages: any[], type: string) {
|
||||
let peerID = this.peerID;
|
||||
|
||||
let sharedMediaDiv: HTMLDivElement;
|
||||
|
||||
let elemsToAppend: HTMLElement[] = [];
|
||||
|
||||
// https://core.telegram.org/type/MessagesFilter
|
||||
switch(type) {
|
||||
case 'inputMessagesFilterPhotoVideo': {
|
||||
sharedMediaDiv = this.sharedMedia.contentMedia;
|
||||
|
||||
for(let message of messages) {
|
||||
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
|
||||
|
||||
let div = document.createElement('div');
|
||||
//console.log(message, photo);
|
||||
|
||||
let isPhoto = media._ == 'photo';
|
||||
|
||||
let photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
|
||||
if(!photo || !photo.downloaded) {
|
||||
let isDownloaded = (photo && photo.downloaded) || appPhotosManager.getDocumentCachedThumb(media.id);
|
||||
if(!isDownloaded) {
|
||||
//this.log('inputMessagesFilterPhotoVideo', message, media, photo, div);
|
||||
|
||||
let sizes = media.sizes || media.thumbs;
|
||||
@ -260,33 +322,39 @@ class AppSidebarRight {
|
||||
}
|
||||
|
||||
//this.log('inputMessagesFilterPhotoVideo', message, media);
|
||||
|
||||
if(!isPhoto) {
|
||||
let span = document.createElement('span');
|
||||
span.classList.add('video-time');
|
||||
div.append(span);
|
||||
|
||||
if(media.type != 'gif') {
|
||||
span.innerText = (media.duration + '').toHHMMSS(false);
|
||||
|
||||
/* let spanPlay = document.createElement('span');
|
||||
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
|
||||
div.append(spanPlay); */
|
||||
} else {
|
||||
span.innerText = 'GIF';
|
||||
}
|
||||
}
|
||||
|
||||
let load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
|
||||
.then((blob) => {
|
||||
.then(() => {
|
||||
if($rootScope.selectedPeerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
return;
|
||||
}
|
||||
|
||||
if(photo && photo.url) {
|
||||
renderImageFromUrl(div, photo.url);
|
||||
} else {
|
||||
let url = URL.createObjectURL(blob);
|
||||
this.urlsToRevoke.push(url);
|
||||
|
||||
let img = new Image();
|
||||
img.src = url;
|
||||
img.onload = () => {
|
||||
div.style.backgroundImage = 'url(' + url + ')';
|
||||
};
|
||||
let url = (photo && photo.url) || appPhotosManager.getDocumentCachedThumb(media.id).url;
|
||||
if(url) {
|
||||
renderImageFromUrl(div, url);
|
||||
}
|
||||
|
||||
//div.style.backgroundImage = 'url(' + url + ')';
|
||||
});
|
||||
|
||||
div.dataset.mid = '' + message.mid;
|
||||
|
||||
if(photo && photo.downloaded) load();
|
||||
if(isDownloaded) load();
|
||||
else this.lazyLoadQueueSidebar.push({div, load});
|
||||
|
||||
this.lastSharedMediaDiv.append(div);
|
||||
@ -310,19 +378,6 @@ class AppSidebarRight {
|
||||
sharedMediaDiv = this.sharedMedia.contentDocuments;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//this.log('come back down to my knees', message);
|
||||
|
||||
let div = wrapDocument(message.media.document, true);
|
||||
elemsToAppend.push(div);
|
||||
}
|
||||
@ -333,10 +388,6 @@ class AppSidebarRight {
|
||||
sharedMediaDiv = this.sharedMedia.contentLinks;
|
||||
|
||||
for(let message of messages) {
|
||||
if(!message.media.webpage || message.media.webpage._ == 'webPageEmpty') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let webpage = message.media.webpage;
|
||||
let div = document.createElement('div');
|
||||
|
||||
@ -391,10 +442,6 @@ class AppSidebarRight {
|
||||
sharedMediaDiv = this.sharedMedia.contentAudio;
|
||||
|
||||
for(let message of messages) {
|
||||
if(!message.media.document || message.media.document.type != 'audio') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let div = wrapAudio(message.media.document, true);
|
||||
elemsToAppend.push(div);
|
||||
}
|
||||
@ -412,8 +459,10 @@ class AppSidebarRight {
|
||||
|
||||
if(elemsToAppend.length) {
|
||||
//window.requestAnimationFrame(() => {
|
||||
elemsToAppend.forEach(el => this.scroll.append(el, false));
|
||||
//elemsToAppend.forEach(el => this.scroll.append(el, false));
|
||||
//});
|
||||
|
||||
sharedMediaDiv.append(...elemsToAppend);
|
||||
}
|
||||
|
||||
if(sharedMediaDiv) {
|
||||
@ -439,6 +488,8 @@ class AppSidebarRight {
|
||||
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes;
|
||||
typesToLoad = typesToLoad.filter(type => !this.loadedAllMedia[type]);
|
||||
if(!typesToLoad.length) return;
|
||||
|
||||
let loadCount = (appPhotosManager.windowH / 130 | 0) * 3; // that's good for all types
|
||||
|
||||
let historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {});
|
||||
|
||||
@ -447,19 +498,35 @@ class AppSidebarRight {
|
||||
|
||||
let history = historyStorage[type] ?? (historyStorage[type] = []);
|
||||
|
||||
let loadCount = (appPhotosManager.windowH / 130 | 0) * 3;
|
||||
|
||||
// render from cache
|
||||
if(history.length && this.usedFromHistory[type] < history.length && this.cleared[type]) {
|
||||
let ids = history.slice(this.usedFromHistory[type], this.usedFromHistory[type] + loadCount);
|
||||
this.log('loadSidebarMedia: will render from cache', this.usedFromHistory[type], history, ids, loadCount);
|
||||
this.usedFromHistory[type] += ids.length;
|
||||
this.performSearchResult(ids, type);
|
||||
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) {
|
||||
this.performSearchResult(messages, type);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// заливать новую картинку сюда только после полной отправки!
|
||||
//let maxID = this.minMediaID[type] || 0;
|
||||
let maxID = history[history.length - 1] || 0;
|
||||
|
||||
let ids = !maxID && appMessagesManager.historiesStorage[peerID]
|
||||
@ -474,7 +541,7 @@ class AppSidebarRight {
|
||||
ids = ids.concat(value.history);
|
||||
history.push(...ids);
|
||||
|
||||
this.log('loadSidebarMedia: search house of glass', type, value, ids, this.cleared);
|
||||
this.log('loadSidebarMedia: search house of glass', type, value, ids);
|
||||
|
||||
if($rootScope.selectedPeerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
@ -484,14 +551,11 @@ class AppSidebarRight {
|
||||
if(value.history.length < loadCount) {
|
||||
this.loadedAllMedia[type] = true;
|
||||
}
|
||||
|
||||
if(this.cleared[type]) {
|
||||
//ids = history;
|
||||
delete this.cleared[type];
|
||||
}
|
||||
|
||||
this.usedFromHistory[type] = history.length;
|
||||
|
||||
if(ids.length) {
|
||||
this.performSearchResult(ids, type);
|
||||
this.performSearchResult(this.filterMessagesByType(ids, type), type);
|
||||
}
|
||||
}, (err) => {
|
||||
this.log.error('load error:', err);
|
||||
@ -550,8 +614,6 @@ class AppSidebarRight {
|
||||
this.urlsToRevoke.length = 0;
|
||||
|
||||
this.sharedMediaTypes.forEach(type => {
|
||||
//this.minMediaID[type] = 0;
|
||||
this.cleared[type] = true;
|
||||
this.usedFromHistory[type] = 0;
|
||||
});
|
||||
|
||||
|
@ -5,14 +5,41 @@ import appChatsManager from "./appChatsManager";
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
import serverTimeManager from "../mtproto/serverTimeManager";
|
||||
|
||||
export type User = {
|
||||
_: 'user',
|
||||
access_hash: string,
|
||||
first_name: string,
|
||||
last_name: string,
|
||||
username: string,
|
||||
flags: number,
|
||||
id: number,
|
||||
phone: string,
|
||||
photo: any,
|
||||
|
||||
status?: Partial<{
|
||||
_: 'userStatusOffline' | 'userStatusOnline' | 'userStatusRecently' | 'userStatusLastWeek' | 'userStatusLastMonth' | 'userStatusEmpty',
|
||||
wasStatus: any,
|
||||
was_online: number,
|
||||
expires: number
|
||||
}>,
|
||||
|
||||
initials?: string,
|
||||
num?: number,
|
||||
pFlags: Partial<{verified: boolean, support: boolean, self: boolean, bot: boolean, min: number, deleted: boolean}>,
|
||||
rFirstName?: string,
|
||||
rFullName?: string,
|
||||
sortName?: string,
|
||||
sortStatus?: number,
|
||||
};
|
||||
|
||||
export class AppUsersManager {
|
||||
public users: any = {};
|
||||
public usernames: any = {};
|
||||
public userAccess: {[x: number]: string} = {};
|
||||
public users: {[userID: number]: User} = {};
|
||||
public usernames: {[username: string]: number} = {};
|
||||
public userAccess: {[userID: number]: string} = {};
|
||||
public cachedPhotoLocations: any = {};
|
||||
public contactsIndex = SearchIndexManager.createIndex();
|
||||
public contactsFillPromise: any;
|
||||
public contactsList: any;
|
||||
public contactsFillPromise: Promise<number[]>;
|
||||
public contactsList: number[];
|
||||
public myID: number;
|
||||
|
||||
constructor() {
|
||||
@ -80,28 +107,27 @@ export class AppUsersManager {
|
||||
});
|
||||
}
|
||||
|
||||
/* public fillContacts () {
|
||||
public fillContacts() {
|
||||
if(this.contactsFillPromise) {
|
||||
return this.contactsFillPromise;
|
||||
}
|
||||
|
||||
return this.contactsFillPromise = MTProto.apiManager.invokeApi('contacts.getContacts', {
|
||||
return this.contactsFillPromise = apiManager.invokeApi('contacts.getContacts', {
|
||||
hash: 0
|
||||
}).then((result: any) => {
|
||||
var userID, searchText;
|
||||
var i;
|
||||
var userID;
|
||||
this.contactsList = [];
|
||||
this.saveApiUsers(result.users);
|
||||
|
||||
for(var i = 0; i < result.contacts.length; i++) {
|
||||
userID = result.contacts[i].user_id
|
||||
result.contacts.forEach((contact: any) => {
|
||||
userID = contact.user_id;
|
||||
this.contactsList.push(userID);
|
||||
//SearchIndexManager.indexObject(userID, getUserSearchText(userID), contactsIndex); WARNING
|
||||
}
|
||||
SearchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex);
|
||||
});
|
||||
|
||||
return this.contactsList;
|
||||
})
|
||||
} */
|
||||
});
|
||||
}
|
||||
|
||||
public getUserSearchText(id: number) {
|
||||
var user = this.users[id];
|
||||
@ -120,32 +146,28 @@ export class AppUsersManager {
|
||||
' ' + serviceText;
|
||||
}
|
||||
|
||||
/* function getContacts (query) {
|
||||
return fillContacts().then(function (contactsList) {
|
||||
if (angular.isString(query) && query.length) {
|
||||
var results = SearchIndexManager.search(query, contactsIndex)
|
||||
var filteredContactsList = []
|
||||
public getContacts(query?: string) {
|
||||
return this.fillContacts().then(contactsList => {
|
||||
if(query) {
|
||||
const results: any = SearchIndexManager.search(query, this.contactsIndex);
|
||||
const filteredContactsList = contactsList.filter(id => !!results[id]);
|
||||
|
||||
for (var i = 0; i < contactsList.length; i++) {
|
||||
if (results[contactsList[i]]) {
|
||||
filteredContactsList.push(contactsList[i])
|
||||
}
|
||||
}
|
||||
contactsList = filteredContactsList
|
||||
contactsList = filteredContactsList;
|
||||
}
|
||||
|
||||
contactsList.sort((userID1: number, userID2: number) => {
|
||||
const sortName1 = (this.users[userID1] || {}).sortName || '';
|
||||
const sortName2 = (this.users[userID2] || {}).sortName || '';
|
||||
if(sortName1 == sortName2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
contactsList.sort(function (userID1, userID2) {
|
||||
var sortName1 = (users[userID1] || {}.sortName) || ''
|
||||
var sortName2 = (users[userID2] || {}.sortName) || ''
|
||||
if (sortName1 == sortName2) {
|
||||
return 0
|
||||
}
|
||||
return sortName1 > sortName2 ? 1 : -1
|
||||
})
|
||||
return sortName1 > sortName2 ? 1 : -1;
|
||||
});
|
||||
|
||||
return contactsList
|
||||
})
|
||||
} */
|
||||
return contactsList;
|
||||
});
|
||||
}
|
||||
|
||||
public resolveUsername(username: string) {
|
||||
return this.usernames[username] || 0;
|
||||
@ -235,7 +257,7 @@ export class AppUsersManager {
|
||||
this.userAccess[id] = accessHash;
|
||||
}
|
||||
|
||||
public getUserStatusForSort(status: any) {
|
||||
public getUserStatusForSort(status: User['status']) {
|
||||
if(status) {
|
||||
var expires = status.expires || status.was_online;
|
||||
if(expires) {
|
||||
@ -255,17 +277,77 @@ export class AppUsersManager {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public getUser(id: any) {
|
||||
public getUser(id: any): User {
|
||||
if(isObject(id)) {
|
||||
return id;
|
||||
}
|
||||
return this.users[id] || {id: id, deleted: true, num: 1, access_hash: this.userAccess[id]};
|
||||
|
||||
return this.users[id] || {id: id, pFlags: {deleted: true}, num: 1, access_hash: this.userAccess[id]} as User;
|
||||
}
|
||||
|
||||
public getSelf() {
|
||||
return this.getUser(this.myID);
|
||||
}
|
||||
|
||||
public getUserStatusString(userID: number) {
|
||||
if(this.isBot(userID)) {
|
||||
return 'bot';
|
||||
}
|
||||
|
||||
let user = this.getUser(userID);
|
||||
if(!user || !user.status) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let str = '';
|
||||
switch(user.status._) {
|
||||
case 'userStatusRecently': {
|
||||
str = 'last seen recently';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'userStatusLastWeek': {
|
||||
str = 'last seen last week';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'userStatusLastMonth': {
|
||||
str = 'last seen last month';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'userStatusOffline': {
|
||||
str = 'last seen ';
|
||||
|
||||
let date = user.status.was_online;
|
||||
let now = Date.now() / 1000;
|
||||
|
||||
if((now - date) < 60) {
|
||||
str += ' just now';
|
||||
} else if((now - date) < 3600) {
|
||||
let c = (now - date) / 60 | 0;
|
||||
str += c + ' ' + (c == 1 ? 'minute' : 'minutes') + ' ago';
|
||||
} else if(now - date < 86400) {
|
||||
let c = (now - date) / 3600 | 0;
|
||||
str += c + ' ' + (c == 1 ? 'hour' : 'hours') + ' ago';
|
||||
} else {
|
||||
let d = new Date(date * 1000);
|
||||
str += ('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2) + ' at ' +
|
||||
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'userStatusOnline': {
|
||||
str = 'online';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public isBot(id: number) {
|
||||
return this.users[id] && this.users[id].pFlags.bot;
|
||||
}
|
||||
@ -345,12 +427,6 @@ export class AppUsersManager {
|
||||
}
|
||||
}
|
||||
|
||||
public wrapForFull(id: number) {
|
||||
var user = this.getUser(id);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/* function importContact (phone, firstName, lastName) {
|
||||
return MtpApiManager.invokeApi('contacts.importContacts', {
|
||||
contacts: [{
|
||||
@ -426,7 +502,7 @@ export class AppUsersManager {
|
||||
flags: 1,
|
||||
correspondents: true,
|
||||
offset: 0,
|
||||
limit: 5,
|
||||
limit: 30,
|
||||
hash: 0,
|
||||
}).then((peers: any) => {
|
||||
//console.log(peers);
|
||||
@ -477,7 +553,7 @@ export class AppUsersManager {
|
||||
|
||||
var user = this.users[userID];
|
||||
if(user) {
|
||||
var status = offline ? {
|
||||
var status: any = offline ? {
|
||||
_: 'userStatusOffline',
|
||||
was_online: tsNow(true)
|
||||
} : {
|
||||
|
@ -66,7 +66,7 @@ export function bytesFromHex(hexString: string) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function bytesToBase64(bytes: number[]) {
|
||||
export function bytesToBase64(bytes: number[] | Uint8Array) {
|
||||
var mod3
|
||||
var result = ''
|
||||
|
||||
|
@ -20,7 +20,7 @@ class LottieLoader {
|
||||
public loadLottie() {
|
||||
if(this.loaded) return this.loaded;
|
||||
|
||||
this.loaded = new Promise((resolve, reject) => {
|
||||
return this.loaded = new Promise((resolve, reject) => {
|
||||
(window as any).lottieLoaded = () => {
|
||||
console.log('lottie loaded');
|
||||
this.lottie = (window as any).lottie;
|
||||
|
@ -59,6 +59,10 @@ export class ApiManager {
|
||||
$rootScope.$broadcast('user_auth', fullUserAuth);
|
||||
/// #endif
|
||||
}
|
||||
|
||||
public setBaseDcID(dcID: number) {
|
||||
this.baseDcID = dcID;
|
||||
}
|
||||
|
||||
// mtpLogOut
|
||||
public async logOut() {
|
||||
@ -68,9 +72,7 @@ export class ApiManager {
|
||||
|
||||
for(let dcID = 1; dcID <= 5; dcID++) {
|
||||
storageKeys.push(prefix + dcID + '_auth_key');
|
||||
//storageKeys.push('dc' + dcID + '_auth_keyID');
|
||||
//storageKeys.push('t_dc' + dcID + '_auth_key');
|
||||
//storageKeys.push('t_dc' + dcID + '_auth_keyID');
|
||||
//storageKeys.push(prefix + dcID + '_auth_keyID');
|
||||
}
|
||||
|
||||
// WebPushApiManager.forceUnsubscribe(); // WARNING
|
||||
@ -95,9 +97,9 @@ export class ApiManager {
|
||||
error.handled = true;
|
||||
this.telegramMeNotify(false);
|
||||
this.mtpClearStorage();
|
||||
}).then(() => {
|
||||
})/* .then(() => {
|
||||
location.pathname = '/';
|
||||
});
|
||||
}) */;
|
||||
}
|
||||
|
||||
public mtpClearStorage() {
|
||||
|
@ -26,15 +26,26 @@ ctx.onmessage = function(e) {
|
||||
ctx.postMessage({taskID: taskID, result: result});
|
||||
});
|
||||
|
||||
default:
|
||||
return apiManager[e.data.task].apply(apiManager, e.data.args).then(result => {
|
||||
//console.log(e.data.task + ' result:', result, taskID);
|
||||
ctx.postMessage({taskID: taskID, result: result});
|
||||
}).catch(err => {
|
||||
//console.error(e.data.task + ' err:', err, taskID);
|
||||
default: {
|
||||
try {
|
||||
let result = apiManager[e.data.task].apply(apiManager, e.data.args);
|
||||
if(result instanceof Promise) {
|
||||
result.then(result => {
|
||||
//console.log(e.data.task + ' result:', result, taskID);
|
||||
ctx.postMessage({taskID: taskID, result: result});
|
||||
}).catch(err => {
|
||||
//console.error(e.data.task + ' err:', err, taskID);
|
||||
ctx.postMessage({taskID: taskID, error: err});
|
||||
});
|
||||
} else {
|
||||
ctx.postMessage({taskID: taskID, result: result});
|
||||
}
|
||||
} catch(err) {
|
||||
ctx.postMessage({taskID: taskID, error: err});
|
||||
});
|
||||
}
|
||||
|
||||
//throw new Error('Unknown task: ' + e.data.task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,10 @@ class ApiManagerProxy extends CryptoWorkerMethods {
|
||||
return this.performTaskWorker('invokeApi', method, params, options);
|
||||
}
|
||||
|
||||
public setBaseDcID(dcID: number) {
|
||||
return this.performTaskWorker('setBaseDcID', dcID);
|
||||
}
|
||||
|
||||
public setUserAuth(userAuth: {id: number}) {
|
||||
$rootScope.$broadcast('user_auth', userAuth);
|
||||
return this.performTaskWorker('setUserAuth', userAuth);
|
||||
|
@ -8,7 +8,7 @@ export class ServerTimeManager {
|
||||
|
||||
public midnightOffset = this.midnightNoOffset - (Math.floor(+this.midnightOffseted / 1000));
|
||||
|
||||
public serverTimeOffset = 0;
|
||||
public serverTimeOffset = 0; // in seconds
|
||||
public timeParams = {
|
||||
midnightOffset: this.midnightOffset,
|
||||
serverTimeOffset: this.serverTimeOffset
|
||||
|
158
src/pages/pageSignQR.ts
Normal file
158
src/pages/pageSignQR.ts
Normal file
@ -0,0 +1,158 @@
|
||||
//import apiManager from '../lib/mtproto/apiManager';
|
||||
import apiManager from '../lib/mtproto/mtprotoworker';
|
||||
import Page from './page';
|
||||
import pageIm from './pageIm';
|
||||
import pagePassword from './pagePassword';
|
||||
import { App } from '../lib/mtproto/mtproto_config';
|
||||
import { bytesToBase64, bytesCmp } from '../lib/bin_utils';
|
||||
import serverTimeManager from '../lib/mtproto/serverTimeManager';
|
||||
import { User } from '../lib/appManagers/appUsersManager';
|
||||
|
||||
/* interface Authorization {
|
||||
_: 'authorization',
|
||||
flags: number,
|
||||
pFlags: Partial<{current: true, official_app: true, password_pending: true}>,
|
||||
hash: number[],
|
||||
device_model: string,
|
||||
platform: string,
|
||||
system_version: string,
|
||||
api_id: number,
|
||||
app_name: string,
|
||||
app_version: string,
|
||||
date_created: number,
|
||||
date_active: number,
|
||||
ip: string,
|
||||
country: string,
|
||||
region: string
|
||||
}; */
|
||||
|
||||
interface AuthAuthorization {
|
||||
flags: number,
|
||||
pFlags: Partial<{tmp_sessions: number}>,
|
||||
user: User
|
||||
}
|
||||
|
||||
interface LoginToken {
|
||||
_: 'auth.loginToken',
|
||||
expires: number,
|
||||
token: Uint8Array
|
||||
};
|
||||
|
||||
interface LoginTokenMigrateTo {
|
||||
_: 'auth.loginTokenMigrateTo',
|
||||
dc_id: number,
|
||||
token: Uint8Array
|
||||
};
|
||||
|
||||
interface LoginTokenSuccess {
|
||||
_: 'auth.loginTokenSuccess',
|
||||
authorization: AuthAuthorization
|
||||
};
|
||||
|
||||
let onFirstMount = async() => {
|
||||
const pageElement = page.pageEl;
|
||||
const imageDiv = pageElement.querySelector('.auth-image') as HTMLDivElement;
|
||||
|
||||
const results = await Promise.all([
|
||||
import('qr-code-styling' as any)
|
||||
]);
|
||||
const QRCodeStyling = results[0].default;
|
||||
|
||||
let stop = false;
|
||||
document.addEventListener('user_auth', () => {
|
||||
stop = true;
|
||||
}, {once: true});
|
||||
|
||||
let options: {dcID?: number} = {};
|
||||
let prevToken: Uint8Array;
|
||||
|
||||
do {
|
||||
if(stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
let loginToken: LoginToken | LoginTokenMigrateTo | LoginTokenSuccess = await apiManager.invokeApi('auth.exportLoginToken', {
|
||||
api_id: App.id,
|
||||
api_hash: App.hash,
|
||||
except_ids: []
|
||||
}/* , options */);
|
||||
|
||||
if(loginToken._ == 'auth.loginTokenMigrateTo') {
|
||||
if(!options.dcID) {
|
||||
options.dcID = loginToken.dc_id;
|
||||
apiManager.setBaseDcID(loginToken.dc_id);
|
||||
//continue;
|
||||
}
|
||||
|
||||
loginToken = await apiManager.invokeApi('auth.importLoginToken', {
|
||||
token: loginToken.token
|
||||
}, options) as LoginToken;
|
||||
}
|
||||
|
||||
if(loginToken._ == 'auth.loginTokenSuccess') {
|
||||
let authorization = loginToken.authorization;
|
||||
apiManager.setUserAuth({
|
||||
id: authorization.user.id
|
||||
});
|
||||
pageIm.mount();
|
||||
break;
|
||||
}
|
||||
|
||||
/* // to base64
|
||||
var decoder = new TextDecoder('utf8');
|
||||
var b64encoded = btoa(String.fromCharCode.apply(null, [...loginToken.token])); */
|
||||
|
||||
if(!prevToken || !bytesCmp(prevToken, loginToken.token)) {
|
||||
prevToken = loginToken.token;
|
||||
|
||||
let encoded = bytesToBase64(loginToken.token);
|
||||
let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "");
|
||||
|
||||
imageDiv.innerHTML = '';
|
||||
const qrCode = new QRCodeStyling({
|
||||
width: 166,
|
||||
height: 166,
|
||||
data: url,
|
||||
image: "assets/img/logo_padded.svg",
|
||||
dotsOptions: {
|
||||
color: "#000000",
|
||||
type: "rounded"
|
||||
},
|
||||
imageOptions: {
|
||||
imageSize: .75
|
||||
},
|
||||
backgroundOptions: {
|
||||
color: "#ffffff"
|
||||
},
|
||||
qrOptions: {
|
||||
errorCorrectionLevel: "L"
|
||||
}
|
||||
});
|
||||
qrCode.append(imageDiv);
|
||||
}
|
||||
|
||||
let timestamp = Date.now() / 1000;
|
||||
let diff = loginToken.expires - timestamp - serverTimeManager.serverTimeOffset;
|
||||
|
||||
await new Promise((resolve, reject) => setTimeout(resolve, diff > 5 ? 5e3 : 1e3 * diff | 0));
|
||||
} catch(err) {
|
||||
switch(err.type) {
|
||||
case 'SESSION_PASSWORD_NEEDED':
|
||||
console.warn('pageSignQR: SESSION_PASSWORD_NEEDED');
|
||||
err.handled = true;
|
||||
pagePassword.mount();
|
||||
break;
|
||||
default:
|
||||
console.error('pageSignQR: default error:', err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(true);
|
||||
};
|
||||
|
||||
const page = new Page('page-signQR', true, () => {
|
||||
onFirstMount();
|
||||
});
|
||||
|
||||
export default page;
|
@ -5,7 +5,6 @@ import pageIm from './pageIm';
|
||||
import apiManager from '../lib/mtproto/mtprotoworker';
|
||||
import apiFileManager from '../lib/mtproto/apiFileManager';
|
||||
import Page from './page';
|
||||
import { calcImageInBox } from '../lib/utils';
|
||||
|
||||
let authCode: {
|
||||
'phone_number': string,
|
||||
|
@ -32,13 +32,6 @@ a {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
// classic clearfix
|
||||
.clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
// Z-levels
|
||||
.z-depth-0 {
|
||||
box-shadow: none !important;
|
||||
@ -82,54 +75,6 @@ a {
|
||||
0 11px 15px -7px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.hoverable {
|
||||
transition: box-shadow .25s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
}
|
||||
|
||||
// Icon Styles
|
||||
|
||||
i {
|
||||
line-height: inherit;
|
||||
|
||||
&.left {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
&.right {
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
}
|
||||
&.tiny {
|
||||
font-size: 1rem;
|
||||
}
|
||||
&.small {
|
||||
font-size: 2rem;
|
||||
}
|
||||
&.medium {
|
||||
font-size: 4rem;
|
||||
}
|
||||
&.large {
|
||||
font-size: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************
|
||||
Transition Classes
|
||||
**********************/
|
||||
|
||||
ul.staggered-list li {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
opacity: 0;
|
||||
transform-origin: 0 50%;
|
||||
}
|
||||
|
||||
/*******************
|
||||
Utility Classes
|
||||
*******************/
|
||||
@ -138,29 +83,11 @@ Utility Classes
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Text Align
|
||||
.left-align {
|
||||
text-align: left;
|
||||
}
|
||||
.right-align {
|
||||
text-align: right
|
||||
}
|
||||
.center, .center-align {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left !important;
|
||||
}
|
||||
.right {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
// No Text Select
|
||||
.no-select {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
.center-align {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -1,39 +1,16 @@
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
html{
|
||||
html {
|
||||
line-height: 1.5;
|
||||
|
||||
/* @media only screen and (min-width: 0) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $medium-screen) {
|
||||
font-size: 14.5px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $large-screen) {
|
||||
font-size: 15px;
|
||||
} */
|
||||
|
||||
font-weight: normal;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 400;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
// Header Styles
|
||||
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; }
|
||||
/* h1 { font-size: $h1-fontsize; line-height: 110%; margin: ($h1-fontsize / 1.5) 0 ($h1-fontsize / 2.5) 0;}
|
||||
h2 { font-size: $h2-fontsize; line-height: 110%; margin: ($h2-fontsize / 1.5) 0 ($h2-fontsize / 2.5) 0;}
|
||||
h3 { font-size: $h3-fontsize; line-height: 110%; margin: ($h3-fontsize / 1.5) 0 ($h3-fontsize / 2.5) 0;}
|
||||
h4 { font-size: $h4-fontsize; line-height: 110%; margin: ($h4-fontsize / 1.5) 0 ($h4-fontsize / 2.5) 0;}
|
||||
h5 { font-size: $h5-fontsize; line-height: 110%; margin: ($h5-fontsize / 1.5) 0 ($h5-fontsize / 2.5) 0;}
|
||||
h6 { font-size: $h6-fontsize; line-height: 110%; margin: ($h6-fontsize / 1.5) 0 ($h6-fontsize / 2.5) 0;} */
|
||||
|
||||
// Text Styles
|
||||
em { font-style: italic; }
|
||||
strong { font-weight: 500; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
$chat-max-width: 696px;
|
||||
$time-background: rgba(0, 0, 0, 0.35);
|
||||
|
||||
#bubble-contextmenu > div {
|
||||
padding: 0 84px 0 16px;
|
||||
@ -75,6 +76,10 @@ $chat-max-width: 696px;
|
||||
|
||||
.chat-more-button {
|
||||
margin-left: 8px;
|
||||
|
||||
.btn-menu {
|
||||
top: calc(100% + 7px);
|
||||
}
|
||||
}
|
||||
|
||||
.chat-info {
|
||||
@ -134,7 +139,7 @@ $chat-max-width: 696px;
|
||||
/* position: absolute;
|
||||
bottom: 0;
|
||||
left: 0; */
|
||||
position: relative;
|
||||
//position: relative; // неизвестно зачем это было
|
||||
|
||||
//display: flex; // for end
|
||||
//flex-direction: unset;
|
||||
@ -163,6 +168,10 @@ $chat-max-width: 696px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
&-container .preloader-circular {
|
||||
background-color: $time-background;
|
||||
}
|
||||
}
|
||||
|
||||
#bubbles-inner {
|
||||
@ -185,6 +194,8 @@ $chat-max-width: 696px;
|
||||
}
|
||||
|
||||
&.is-channel:not(.is-chat) {
|
||||
padding-bottom: 55px;
|
||||
|
||||
.bubble__container {
|
||||
max-width: 100%;
|
||||
}
|
||||
@ -250,9 +261,11 @@ $chat-max-width: 696px;
|
||||
|
||||
.bubble {
|
||||
padding-top: 5px;
|
||||
display: grid;
|
||||
/* display: grid;
|
||||
grid-template-columns: 1fr $chat-max-width 1fr;
|
||||
grid-row-gap: 0px;
|
||||
grid-row-gap: 0px; */
|
||||
max-width: $chat-max-width;
|
||||
margin: 0 auto;
|
||||
|
||||
&.is-date {
|
||||
position: -webkit-sticky;
|
||||
@ -268,10 +281,10 @@ $chat-max-width: 696px;
|
||||
}
|
||||
}
|
||||
|
||||
&:before, &:after {
|
||||
/* &:before, &:after {
|
||||
content: " ";
|
||||
width: 100%;
|
||||
}
|
||||
} */
|
||||
|
||||
&__container {
|
||||
//min-width: 60px;
|
||||
@ -303,7 +316,8 @@ $chat-max-width: 696px;
|
||||
padding: 5px 0;
|
||||
|
||||
.bubble__container {
|
||||
justify-self: center;
|
||||
/* justify-self: center; */
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
@ -536,6 +550,8 @@ $chat-max-width: 696px;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
|
||||
.download {
|
||||
@ -550,15 +566,15 @@ $chat-max-width: 696px;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
background-color: rgba(0, 0, 0, .7);
|
||||
border-radius: 50%;
|
||||
background-color: $time-background;
|
||||
font-size: 23px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
& ~ .video-play {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -894,7 +910,7 @@ $chat-max-width: 696px;
|
||||
bottom: .1rem;
|
||||
right: .2rem;
|
||||
border-radius: 12px;
|
||||
background-color: rgba(0, 0, 0, .4);
|
||||
background-color: $time-background;
|
||||
padding: 0 .2rem;
|
||||
z-index: 2;
|
||||
|
||||
@ -948,6 +964,32 @@ $chat-max-width: 696px;
|
||||
}
|
||||
}
|
||||
|
||||
span.video-time {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
border-radius: 12px;
|
||||
background-color: $time-background;
|
||||
padding: 0px 6px 0px 6px;
|
||||
z-index: 2;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
span.video-play {
|
||||
background-color: $time-background;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 34px;
|
||||
line-height: 60px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.is-edited.channel-post .time {
|
||||
min-width: calc(5rem + 46px);
|
||||
}
|
||||
@ -998,6 +1040,10 @@ $chat-max-width: 696px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.webpage):not(.is-album):not(.sticker):not(.round) .attachment, .album-item {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
&.hide-name:not(.is-reply):not(.is-message-empty) .message {
|
||||
//padding-top: .2675rem;
|
||||
@ -1141,6 +1187,22 @@ $chat-max-width: 696px;
|
||||
.audio-subtitle, .contact-number, .audio-time {
|
||||
color: #707579 !important;
|
||||
}
|
||||
|
||||
.message.audio-message {
|
||||
.media-progress {
|
||||
&__seek {
|
||||
background: rgba(193, 207, 220, 0.39);
|
||||
}
|
||||
|
||||
&__filled {
|
||||
background-color: #0089ff;
|
||||
}
|
||||
|
||||
input::-webkit-slider-thumb {
|
||||
background: #63a2e3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-out {
|
||||
|
@ -262,46 +262,57 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&:not(.search-group-messages) {
|
||||
.user-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
&-contacts {
|
||||
padding: 16px 0 7px;
|
||||
|
||||
li {
|
||||
//margin-bottom: 2px;
|
||||
padding-bottom: 4px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
li > .rp {
|
||||
padding: 9px 11.5px !important;
|
||||
height: 66px;
|
||||
}
|
||||
|
||||
.search-group__name {
|
||||
padding-bottom: 17px;
|
||||
}
|
||||
|
||||
.user-caption {
|
||||
padding: 1px 3.5px 1px 13px;
|
||||
}
|
||||
|
||||
.user-title, b, .user-last-message b {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
span.user-last-message {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use together like class="chats-container contacts-container"
|
||||
.contacts-container, .search-group-contacts {
|
||||
.user-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
li {
|
||||
//margin-bottom: 2px;
|
||||
padding-bottom: 4px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
li > .rp {
|
||||
padding: 9px 11.5px !important;
|
||||
height: 66px;
|
||||
}
|
||||
|
||||
.user-caption {
|
||||
padding: 1px 3.5px 1px 13px;
|
||||
}
|
||||
|
||||
.user-title, b, .user-last-message b {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
span.user-last-message {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
#contacts-container {
|
||||
.sidebar-header {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.input-search {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
@ -49,42 +49,57 @@
|
||||
.tabs-container {
|
||||
/* width: 300%; */
|
||||
height: 100%;
|
||||
|
||||
.category-title {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
font-size: .85rem;
|
||||
color: $color-gray;
|
||||
background: linear-gradient(to bottom,#fff 0,rgba(255,255,255,.9) 60%,rgba(255,255,255,0) 100%);
|
||||
z-index: 2;
|
||||
padding: .53333rem 6PX .66667rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.emoji-category {
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.25rem;
|
||||
padding-top: 1px;
|
||||
position: relative;
|
||||
|
||||
display: grid;
|
||||
grid-column-gap: 2.44px;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
.category-items {
|
||||
display: grid;
|
||||
grid-column-gap: 2.44px;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.25rem;
|
||||
|
||||
> * {
|
||||
margin: 0;
|
||||
padding: 4px 4px;
|
||||
line-height: inherit;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
|
||||
.emoji {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(112, 117, 121, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
> * {
|
||||
margin: 0;
|
||||
padding: 4px 4px;
|
||||
line-height: inherit;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
|
||||
.emoji {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(112, 117, 121, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* &::after {
|
||||
content: "";
|
||||
flex: auto;
|
||||
@ -92,58 +107,53 @@
|
||||
}
|
||||
|
||||
.sticker-category {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.sticker-category {
|
||||
|
||||
/* &.not-full::after {
|
||||
content: "";
|
||||
flex: auto;
|
||||
} */
|
||||
|
||||
> div {
|
||||
.category-items {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
> div > div {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* overflow: hidden; */
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
/* margin: 3.5px 0;
|
||||
margin-right: 6.25px; */
|
||||
padding: 1px 2.5px;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(112, 117, 121, 0.08);
|
||||
}
|
||||
|
||||
/* &:nth-child(5n+5) {
|
||||
margin-right: 0;
|
||||
} */
|
||||
|
||||
> * {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
> div {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* overflow: hidden; */
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
/* margin: 3.5px 0;
|
||||
margin-right: 6.25px; */
|
||||
padding: 1px 2.5px;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(112, 117, 121, 0.08);
|
||||
}
|
||||
|
||||
/* &:nth-child(5n+5) {
|
||||
margin-right: 0;
|
||||
} */
|
||||
|
||||
> * {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,3 +4,4 @@ $tgico-font-path: "../../assets/fonts" !default;
|
||||
$tgico-check: "\e900";
|
||||
$tgico-checks: "\e95a";
|
||||
$tgico-sending: "\e919";
|
||||
$tgico-close: "\e943";
|
||||
|
@ -8,6 +8,10 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar-slider {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar-header__btn-container {
|
||||
position: relative;
|
||||
width: 39.75px;
|
||||
@ -67,5 +71,70 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.search-group-people {
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-left: 4px;
|
||||
margin-top: -1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-right: 5px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rp {
|
||||
height: 98px;
|
||||
max-height: 98px;
|
||||
border-radius: 10px;
|
||||
max-width: 78px;
|
||||
width: 78px;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
padding: 12px 0 0 !important;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.user-caption {
|
||||
max-width: 65px;
|
||||
padding: 2px 0px 9px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.user-title {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.search-group-scrollable {
|
||||
position: relative;
|
||||
|
||||
> .scrollable {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#search-container {
|
||||
transition: 150ms ease-in-out opacity,150ms ease-in-out transform;
|
||||
transform: scale(1.1, 1.1);
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
|
||||
&.active {
|
||||
transform: scale(1, 1);
|
||||
transform-origin: center;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
@ -122,12 +122,12 @@
|
||||
.media-viewer-caption {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: $darkgrey;
|
||||
transition: .2s;
|
||||
max-width: 50vw;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
|
@ -36,6 +36,14 @@
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-search {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profile {
|
||||
@ -201,7 +209,7 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 4px;
|
||||
padding: 4px 7.5px 7.5px;
|
||||
|
||||
> div {
|
||||
display: grid;
|
||||
@ -220,9 +228,8 @@
|
||||
background-position: center center;
|
||||
|
||||
display: flex;
|
||||
//background-color: #000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #000;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
@ -233,6 +240,28 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span.video-time {
|
||||
position: relative;
|
||||
left: 5px;
|
||||
top: 4px;
|
||||
height: 18px;
|
||||
border-radius: 4px;
|
||||
background-color: $time-background;
|
||||
padding: 0px 6px 0px 5px;
|
||||
z-index: 2;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* span.video-play {
|
||||
background-color: $time-background;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 34px;
|
||||
line-height: 60px;
|
||||
cursor: pointer;
|
||||
} */
|
||||
}
|
||||
|
||||
#content-docs {
|
||||
|
151
src/scss/partials/_selector.scss
Normal file
151
src/scss/partials/_selector.scss
Normal file
@ -0,0 +1,151 @@
|
||||
@keyframes scaleIn {
|
||||
0% {
|
||||
transform: scale(.2);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.selector {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&-search-container {
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
max-height: 132px;
|
||||
|
||||
.scrollable {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
&-search {
|
||||
padding: 0 24px 0 24px;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
|
||||
input {
|
||||
border: none;
|
||||
padding: 7px 0px 19px 0px;
|
||||
outline: none;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-user {
|
||||
color: #000;
|
||||
background-color: rgba(112, 117, 121, 0.08);
|
||||
font-size: 16px;
|
||||
padding: 0 17px 0px 0px;
|
||||
line-height: 31px;
|
||||
margin-left: -4px;
|
||||
margin-right: 12px;
|
||||
height: 32px;
|
||||
margin-bottom: 7px;
|
||||
border-radius: 24px;
|
||||
user-select: none;
|
||||
flex: 0 0 auto;
|
||||
transition: .2s all;
|
||||
|
||||
&:hover {
|
||||
background-color: #fae2e3;
|
||||
cursor: pointer;
|
||||
|
||||
.user-avatar:after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.scale-in {
|
||||
animation: scaleIn .15s ease forwards;
|
||||
}
|
||||
|
||||
&.scale-out {
|
||||
animation: scaleIn .1s ease forwards;
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
height: 32px !important;
|
||||
width: 32px !important;
|
||||
float: left;
|
||||
margin-right: 8px;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
content: $tgico-close;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: #df3f40;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
font-size: 23px;
|
||||
line-height: 32px;
|
||||
opacity: 0;
|
||||
transition: .2s opacity;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chats-container {
|
||||
height: 100%;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
.user-avatar {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.user-caption {
|
||||
padding: 1px 3.5px 1px 12px;
|
||||
}
|
||||
|
||||
p {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
span.user-title {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
span.user-last-message {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-bottom: 0;
|
||||
|
||||
> .rp {
|
||||
margin: 0px 9px 0px 8px;
|
||||
padding: 12px 8.5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border: none;
|
||||
background-color: #DADCE0;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
[type="checkbox"] + span {
|
||||
padding-left: calc(9px + 2.25rem);
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-top: 11px;
|
||||
padding-left: 11px;
|
||||
}
|
||||
}
|
79
src/scss/partials/_slider.scss
Normal file
79
src/scss/partials/_slider.scss
Normal file
@ -0,0 +1,79 @@
|
||||
.menu-horizontal {
|
||||
color: $darkgrey;
|
||||
border-bottom: 1px solid $lightgrey;
|
||||
position: relative;
|
||||
|
||||
ul {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding: .75rem 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
user-select: none;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
|
||||
&.active {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&__stripe {
|
||||
position: absolute;
|
||||
background: $blue;
|
||||
//left: 0;
|
||||
left: -2px;
|
||||
transition: .3s transform, .3s width;
|
||||
//transition: .3s transform;
|
||||
bottom: -1px;
|
||||
height: 4px;
|
||||
width: 1px; // need if using transform
|
||||
transform: scaleX(1) translateX(0px);
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
/* overflow: hidden; */
|
||||
overflow-x: hidden;
|
||||
|
||||
&.animated {
|
||||
transition: .3s transform;
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
/* transition: .2s all; */
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
> div:not(.scroll-padding) {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
/* overflow: hidden; */
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,6 +42,8 @@ $large-screen: 1680px;
|
||||
@import "partials/ckin";
|
||||
@import "partials/emojiDropdown";
|
||||
@import "partials/scrollable";
|
||||
@import "partials/slider";
|
||||
@import "partials/selector";
|
||||
|
||||
@import "partials/popups/popup";
|
||||
@import "partials/popups/editAvatar";
|
||||
@ -169,6 +171,19 @@ input {
|
||||
}
|
||||
}
|
||||
|
||||
.btn-corner {
|
||||
position: absolute !important;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
transition: .2s ease;
|
||||
transform: translateY(calc(100% + 20px));
|
||||
z-index: 3;
|
||||
|
||||
&.is-visible {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: $color-error!important;
|
||||
}
|
||||
@ -263,6 +278,16 @@ input {
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
color: #fff;
|
||||
width: 54px;
|
||||
@ -286,6 +311,10 @@ input {
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
user-select: none;
|
||||
|
||||
&.fade-in {
|
||||
animation: fadeIn .2s ease forwards;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*=" tgico-"] {
|
||||
@ -624,6 +653,23 @@ input {
|
||||
}
|
||||
}
|
||||
|
||||
/* .page-signQR {
|
||||
.auth-image {
|
||||
position: relative;
|
||||
|
||||
.sign-logo {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
.page-signUp {
|
||||
.auth-image {
|
||||
border-radius: 50%;
|
||||
@ -837,6 +883,7 @@ input {
|
||||
|
||||
&:not(:checked) + span:after {
|
||||
background-color: transparent;
|
||||
border-color: #8d969c;
|
||||
}
|
||||
|
||||
&:checked + span:after {
|
||||
@ -997,19 +1044,19 @@ input:focus, button:focus {
|
||||
}
|
||||
}
|
||||
|
||||
/* // scaling... any units
|
||||
$width: 100px;
|
||||
|
||||
.loader {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: $width;
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-top: 100%;
|
||||
.btn-primary.btn-circle {
|
||||
.preloader-circular {
|
||||
height: calc(100% - 20px);
|
||||
right: auto;
|
||||
left: auto;
|
||||
margin: 0;
|
||||
top: 10px;
|
||||
|
||||
.preloader-path {
|
||||
stroke: #fff;
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
.preloader {
|
||||
&-circular {
|
||||
@ -1164,6 +1211,7 @@ img.emoji {
|
||||
border-radius: 50%;
|
||||
height: 54px;
|
||||
width: 54px;
|
||||
line-height: 54px;
|
||||
|
||||
path {
|
||||
fill: white;
|
||||
@ -1271,91 +1319,17 @@ img.emoji {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.menu-horizontal {
|
||||
color: $darkgrey;
|
||||
border-bottom: 1px solid $lightgrey;
|
||||
position: relative;
|
||||
|
||||
ul {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding: .75rem 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
user-select: none;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
|
||||
&.active {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&__stripe {
|
||||
position: absolute;
|
||||
background: $blue;
|
||||
//left: 0;
|
||||
left: -2px;
|
||||
transition: .3s transform, .3s width;
|
||||
//transition: .3s transform;
|
||||
bottom: -1px;
|
||||
height: 4px;
|
||||
width: 1px; // need if using transform
|
||||
transform: scaleX(1) translateX(0px);
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
/* overflow: hidden; */
|
||||
overflow-x: hidden;
|
||||
|
||||
&.animated {
|
||||
transition: .3s transform;
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
/* transition: .2s all; */
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
> div:not(.scroll-padding) {
|
||||
width: 100%;
|
||||
padding: 7.5px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.justify-start {
|
||||
justify-content: flex-start!important;
|
||||
}
|
||||
|
||||
.position-center {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.page-chats {
|
||||
/* display: grid; */
|
||||
/* grid-template-columns: 25% 50%; */
|
||||
@ -1396,8 +1370,7 @@ img.emoji {
|
||||
}
|
||||
}
|
||||
|
||||
#search-container, #chats-archived-container, .sidebar-search {
|
||||
display: none;
|
||||
#search-container, .sidebar-search {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
@ -1408,10 +1381,6 @@ img.emoji {
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
background: #fff;
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $large-screen) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user