Poll vote & update & scroll chat fix & QR page & chat list updates

This commit is contained in:
morethanwords 2020-05-10 04:23:21 +03:00
parent a884a9cea8
commit a6d9d69909
15 changed files with 765 additions and 536 deletions

View File

@ -1,5 +1,6 @@
import appPollsManager, { PollResults } from "../lib/appManagers/appPollsManager"; import appPollsManager, { PollResults, Poll } from "../lib/appManagers/appPollsManager";
import { RichTextProcessor } from "../lib/richtextprocessor"; import { RichTextProcessor } from "../lib/richtextprocessor";
import { findUpClassName, $rootScope } from "../lib/utils";
let lineTotalLength = 0; let lineTotalLength = 0;
const tailLength = 9; const tailLength = 9;
@ -7,113 +8,8 @@ const times = 10;
const fullTime = 340; const fullTime = 340;
const oneTime = fullTime / times; const oneTime = fullTime / times;
export default class PollElement extends HTMLElement { let roundPercents = (percents: number[]) => {
private svgLines: SVGSVGElement[]; //console.log('roundPercents before percents:', percents);
private numberDivs: HTMLDivElement[];
private maxOffset = -44.8;
private maxLength: number;
private maxLengths: number[];
constructor() {
super();
// элемент создан
}
connectedCallback() {
// браузер вызывает этот метод при добавлении элемента в документ
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
if(!lineTotalLength) {
lineTotalLength = (document.getElementById('poll-line') as any as SVGPathElement).getTotalLength();
console.log('line total length:', lineTotalLength);
}
let pollID = this.getAttribute('poll-id');
let {poll, results} = appPollsManager.getPoll(pollID);
console.log('pollElement poll:', poll, results);
let desc = '';
if(poll.pFlags) {
if(poll.pFlags.closed) {
desc = 'Final results';
} else {
desc = poll.pFlags.public_voters ? 'Public Poll' : 'Anonymous Poll';
}
}
let votes = poll.answers.map(answer => {
return `
<div class="poll-answer">
<div class="circle-hover">
<div class="animation-ring"></div>
<svg class="progress-ring">
<circle class="progress-ring__circle" cx="13" cy="13" r="9"></circle>
</svg>
</div>
<div class="poll-answer-percents"></div>
<svg version="1.1" class="poll-line" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 480 28" xml:space="preserve">
<use href="#poll-line"></use>
</svg>
<div class="poll-answer-text">${RichTextProcessor.wrapEmojiText(answer.text)}</div>
</div>
`;
}).join('');
this.innerHTML = `
<div class="poll-title">${poll.rQuestion}</div>
<div class="poll-desc">${desc}</div>
${votes}
<div class="poll-votes-count">${results.total_voters ? results.total_voters + ' voters' : 'No votes'}</div>
`;
let width = this.getBoundingClientRect().width;
this.maxLength = width + tailLength + this.maxOffset + -9; // 13 - position left
this.performResults(results);
}
disconnectedCallback() {
// браузер вызывает этот метод при удалении элемента из документа
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
}
static get observedAttributes(): string[] {
return [/* массив имён атрибутов для отслеживания их изменений */];
}
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
// вызывается при изменении одного из перечисленных выше атрибутов
}
adoptedCallback() {
// вызывается, когда элемент перемещается в новый документ
// (происходит в document.adoptNode, используется очень редко)
}
performResults(results: PollResults) {
const percents = results.results.map(v => v.voters / results.total_voters * 100);
this.setResults(percents);
}
setResults(percents: number[]) {
if(!this.svgLines) {
this.svgLines = Array.from(this.querySelectorAll('.poll-line')) as SVGSVGElement[];
this.numberDivs = Array.from(this.querySelectorAll('.poll-answer-percents')) as HTMLDivElement[];
}
let maxValue = Math.max(...percents);
this.maxLengths = percents.map(p => p / maxValue * this.maxLength);
/* this.svgLines.forEach((svg, idx) => {
this.setLineProgress(idx, 1);
}); */
/* percents = percents.map(p => {
return Math.round(p);
}); */
console.log('setResults before percents:', percents);
let sum = percents.reduce((acc, p) => acc + Math.round(p), 0); let sum = percents.reduce((acc, p) => acc + Math.round(p), 0);
if(sum > 100) { if(sum > 100) {
@ -135,7 +31,7 @@ export default class PollElement extends HTMLElement {
percents[minIndex] -= minRemainder; percents[minIndex] -= minRemainder;
} }
} else { } else if(sum < 100) {
let diff = 100 - sum; let diff = 100 - sum;
let length = percents.length; let length = percents.length;
for(let i = 0; i < diff; ++i) { for(let i = 0; i < diff; ++i) {
@ -156,43 +52,264 @@ export default class PollElement extends HTMLElement {
} }
} }
console.log('setResults after percents:', percents, sum); //console.log('roundPercents after percents:', percents);
};
let start = Date.now(); const connectedPolls: {id: string, element: PollElement}[] = [];
let r = () => { $rootScope.$on('poll_update', (e: CustomEvent) => {
let diff = Date.now() - start; let {poll, results} = e.detail as {poll: Poll, results: PollResults};
let progress = diff / fullTime;
if(progress > 1) progress = 1;
this.svgLines.forEach((svg, idx) => { for(let connected of connectedPolls) {
this.setLineProgress(idx, progress); if(connected.id == poll.id) {
}); let pollElement = connected.element;
pollElement.performResults(results, poll.chosenIndex);
if(progress < 1) {
window.requestAnimationFrame(r);
} }
}; }
window.requestAnimationFrame(r); });
export default class PollElement extends HTMLElement {
private svgLines: SVGSVGElement[];
private numberDivs: HTMLDivElement[];
private selectedSpan: HTMLSpanElement;
private answerDivs: HTMLDivElement[];
private votersCountDiv: HTMLDivElement;
private maxOffset = -46.5;
private maxLength: number;
private maxLengths: number[];
private isQuiz = false;
private isRetracted = false;
private chosenIndex = -1;
private percents: number[];
constructor() {
super();
// элемент создан
}
connectedCallback() {
// браузер вызывает этот метод при добавлении элемента в документ
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
if(!lineTotalLength) {
lineTotalLength = (document.getElementById('poll-line') as any as SVGPathElement).getTotalLength();
console.log('line total length:', lineTotalLength);
}
let pollID = this.getAttribute('poll-id');
let {poll, results} = appPollsManager.getPoll(pollID);
connectedPolls.push({id: pollID, element: this});
console.log('pollElement poll:', poll, results);
let desc = '';
if(poll.pFlags) {
if(poll.pFlags.closed) {
desc = 'Final results';
} else {
if(poll.pFlags.quiz) {
this.isQuiz = true;
}
let type = this.isQuiz ? 'Quiz' : 'Poll';
desc = (poll.pFlags.public_voters ? 'Public' : 'Anonymous') + ' ' + type;
}
}
let votes = poll.answers.map((answer, idx) => {
return `
<div class="poll-answer" data-index="${idx}">
<div class="circle-hover">
<div class="animation-ring"></div>
<svg class="progress-ring">
<circle class="progress-ring__circle" cx="13" cy="13" r="9"></circle>
</svg>
</div>
<div class="poll-answer-percents"></div>
<svg version="1.1" class="poll-line" style="display: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 480 35" xml:space="preserve">
<use href="#poll-line"></use>
</svg>
<div class="poll-answer-text">${RichTextProcessor.wrapEmojiText(answer.text)}</div>
</div>
`;
}).join('');
this.innerHTML = `
<div class="poll-title">${poll.rQuestion}</div>
<div class="poll-desc">${desc}</div>
${votes}
<div class="poll-votes-count"></div>
`;
this.answerDivs = Array.from(this.querySelectorAll('.poll-answer')) as HTMLDivElement[];
this.votersCountDiv = this.querySelector('.poll-votes-count') as HTMLDivElement;
this.svgLines = Array.from(this.querySelectorAll('.poll-line')) as SVGSVGElement[];
this.numberDivs = Array.from(this.querySelectorAll('.poll-answer-percents')) as HTMLDivElement[];
let width = this.getBoundingClientRect().width;
this.maxLength = width + tailLength + this.maxOffset + -13.7; // 13 - position left
if(poll.chosenIndex !== -1) {
this.performResults(results, poll.chosenIndex);
} else {
this.setVotersCount(results);
this.addEventListener('click', this.clickHandler);
}
}
disconnectedCallback() {
// браузер вызывает этот метод при удалении элемента из документа
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
connectedPolls.findAndSplice(c => c.element == this);
}
static get observedAttributes(): string[] {
return [/* массив имён атрибутов для отслеживания их изменений */];
}
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
// вызывается при изменении одного из перечисленных выше атрибутов
}
adoptedCallback() {
// вызывается, когда элемент перемещается в новый документ
// (происходит в document.adoptNode, используется очень редко)
}
clickHandler(e: MouseEvent) {
let target = findUpClassName(e.target, 'poll-answer') as HTMLElement;
if(!target) {
return;
}
let answerIndex = +target.dataset.index;
this.sendVote(answerIndex);
/* target.classList.add('is-voting');
setTimeout(() => { // simulate
this.setResults([100, 0], answerIndex);
target.classList.remove('is-voting');
}, 1000); */
}
sendVote(index: number) {
let target = this.answerDivs[index];
target.classList.add('is-voting');
let mid = +this.getAttribute('message-id');
this.classList.add('disable-hover');
appPollsManager.sendVote(mid, [index]).then(() => {
target.classList.remove('is-voting');
this.classList.remove('disable-hover');
});
}
performResults(results: PollResults, chosenIndex: number) {
if(this.chosenIndex != chosenIndex) { // if we voted
this.isRetracted = this.chosenIndex != -1 && chosenIndex == -1;
this.chosenIndex = chosenIndex;
if(this.isRetracted) {
this.addEventListener('click', this.clickHandler);
} else {
this.removeEventListener('click', this.clickHandler);
}
}
// is need update
if(this.chosenIndex != -1 || this.isRetracted) {
const percents = results.results.map(v => v.voters / results.total_voters * 100);
this.setResults(this.isRetracted ? this.percents : percents, chosenIndex);
this.percents = percents;
this.isRetracted = false;
}
this.setVotersCount(results);
}
setResults(percents: number[], chosenIndex: number) {
this.svgLines.forEach(svg => svg.style.display = '');
if(chosenIndex !== -1) {
let answerDiv = this.answerDivs[chosenIndex];
if(!this.selectedSpan) {
this.selectedSpan = document.createElement('span');
this.selectedSpan.classList.add('poll-answer-selected', 'tgico-check');
}
answerDiv.append(this.selectedSpan);
}
let maxValue = Math.max(...percents);
this.maxLengths = percents.map(p => p / maxValue * this.maxLength);
// line
if(this.isRetracted) {
this.svgLines.forEach((svg, idx) => {
this.setLineProgress(idx, -1);
});
} else {
this.svgLines.forEach((svg, idx) => {
void svg.getBoundingClientRect(); // reflow
this.setLineProgress(idx, 1);
});
}
percents = percents.slice();
roundPercents(percents);
// numbers
if(this.isRetracted) {
for(let i = (times - 1), k = 0; i >= 0; --i, ++k) {
setTimeout(() => {
percents.forEach((percents, idx) => {
let value = Math.round(percents / times * i);
this.numberDivs[idx].innerText = value + '%';
});
}, oneTime * k);
}
} else {
for(let i = 0; i < times; ++i) { for(let i = 0; i < times; ++i) {
setTimeout(() => { setTimeout(() => {
percents.forEach((percents, idx) => { percents.forEach((percents, idx) => {
let value = Math.round(percents / times * (i + 1)); let value = Math.round(percents / times * (i + 1));
let div = this.numberDivs[idx]; this.numberDivs[idx].innerText = value + '%';
//div.style.opacity = ((i + 1) * 0.10).toFixed(1); // опасити в 10 шагов от 0.1 до 1
div.innerText = value + '%';
}); });
}, oneTime * i); }, oneTime * i);
} }
}
if(this.isRetracted) {
this.classList.add('is-retracting');
this.classList.remove('is-voted');
setTimeout(() => {
this.classList.remove('is-retracting');
this.svgLines.forEach(svg => svg.style.display = 'none');
}, fullTime);
} else {
this.classList.add('is-voted'); this.classList.add('is-voted');
} }
}
setVotersCount(results: PollResults) {
let votersCount = results.total_voters || 0;
let votersOrAnswers = this.isQuiz ? (votersCount > 1 || !votersCount ? 'answers' : 'answer') : (votersCount > 1 || !votersCount ? 'votes' : 'vote');
this.votersCountDiv.innerText = `${results.total_voters ? results.total_voters + ' ' + votersOrAnswers : 'No ' + votersOrAnswers}`;
}
setLineProgress(index: number, percents: number) { setLineProgress(index: number, percents: number) {
let svg = this.svgLines[index]; let svg = this.svgLines[index];
if(percents == -1) {
svg.style.strokeDasharray = '';
svg.style.strokeDashoffset = '';
} else {
svg.style.strokeDasharray = (percents * this.maxLengths[index]) + ', 485.9'; svg.style.strokeDasharray = (percents * this.maxLengths[index]) + ', 485.9';
svg.style.strokeDashoffset = '' + percents * this.maxOffset; svg.style.strokeDashoffset = '' + percents * this.maxOffset;
} }
}
// у элемента могут быть ещё другие методы и свойства // у элемента могут быть ещё другие методы и свойства
} }

View File

@ -1,5 +1,7 @@
import { logger, deferredPromise, CancellablePromise } from "../lib/polyfill"; import { logger, deferredPromise, CancellablePromise } from "../lib/polyfill";
import smoothscroll from '../lib/smoothscroll';
(window as any).__forceSmoothScrollPolyfill__ = true;
smoothscroll.polyfill();
/* /*
var el = $0; var el = $0;
var height = 0; var height = 0;
@ -66,6 +68,8 @@ export default class Scrollable {
private lastBottomID = 0; private lastBottomID = 0;
private lastScrollDirection = 0; // true = bottom private lastScrollDirection = 0; // true = bottom
private scrollLocked = 0;
private setVisible(element: HTMLElement) { private setVisible(element: HTMLElement) {
if(this.visible.has(element)) return; if(this.visible.has(element)) return;
@ -152,61 +156,6 @@ export default class Scrollable {
} }
}); });
// внизу - самый производительный вариант
if(false) this.observer = new IntersectionObserver(entries => {
entries/* .filter(entry => entry.isIntersecting) */.forEach((entry, idx, arr) => {
let target = entry.target as HTMLElement;
if(entry.isIntersecting) {
let isTop = entry.boundingClientRect.top <= 0;
let isBottom = entry.rootBounds.height <= (entry.boundingClientRect.top + entry.boundingClientRect.height);
/* let id = +target.dataset.virtual;
let isOutOfRange = id < (this.lastTopID - 15) || id > (this.lastBottomID + 15);
if(isOutOfRange) {
this.debug && this.log('out of range, scroll jumped!');
if(idx == 0) this.lastTopID = id;
else if(idx == (arr.length - 1)) this.lastBottomID = id;
} */
this.setVisible(target);
if(isTop) {
/* this.lastTopID = id;
this.debug && this.log('set lastTopID to:', this.lastTopID); */
for(let i = 0; i < 15; ++i) {
target = target.previousElementSibling as HTMLElement;
if(!target) break;
this.setVisible(target);
}
} else if(isBottom) {
/* this.lastBottomID = id;
this.debug && this.log('set lastBottomID to:', this.lastBottomID); */
for(let i = 0; i < 15; ++i) {
target = target.nextElementSibling as HTMLElement;
if(!target) break;
this.setVisible(target);
}
}
} else {
this.setHidden(target);
}
//this.debug && this.log('intersection entry:', entry, isTop, isBottom, this.lastTopID, this.lastBottomID);
});
/* let minVisibleID = this.lastTopID - 15;
let maxVisibleID = this.lastBottomID + 15;
for(let target of this.visible) {
let id = +target.dataset.virtual;
if(id < minVisibleID || id > maxVisibleID) {
this.setHidden(target);
}
} */
});
if(!appendTo) { if(!appendTo) {
this.appendTo = this.container; this.appendTo = this.container;
} }
@ -326,7 +275,7 @@ export default class Scrollable {
let scrollTop = scrollPos - this.scrollTopOffset; let scrollTop = scrollPos - this.scrollTopOffset;
let maxScrollTop = this.scrollSize - this.scrollTopOffset - this.size; let maxScrollTop = this.scrollSize - this.scrollTopOffset - this.size;
if(this.onScrolledBottom) { if(this.onScrolledBottom && !this.scrollLocked) {
if((maxScrollTop - scrollTop) <= this.onScrollOffset) { if((maxScrollTop - scrollTop) <= this.onScrollOffset) {
//if(!this.onScrolledBottomFired) { //if(!this.onScrolledBottomFired) {
this.onScrolledBottomFired = true; this.onScrolledBottomFired = true;
@ -337,7 +286,7 @@ export default class Scrollable {
} }
} }
if(this.onScrolledTop) { if(this.onScrolledTop && !this.scrollLocked) {
//this.log('onScrolledTop:', scrollTop, this.onScrollOffset); //this.log('onScrolledTop:', scrollTop, this.onScrollOffset);
if(scrollTop <= this.onScrollOffset) { if(scrollTop <= this.onScrollOffset) {
this.onScrolledTopFired = true; this.onScrolledTopFired = true;
@ -357,6 +306,23 @@ export default class Scrollable {
}); });
} }
public reorder() {
(Array.from(this.splitUp.children) as HTMLElement[]).forEach((el, idx) => {
el.dataset.virtual = '' + idx;
});
}
public updateElement(element: HTMLElement) {
element.style.minHeight = '';
window.requestAnimationFrame(() => {
let height = element.scrollHeight;
window.requestAnimationFrame(() => {
element.style.minHeight = height + 'px';
});
});
}
public prepareElement(element: HTMLElement, append = true) { public prepareElement(element: HTMLElement, append = true) {
element.dataset.virtual = '' + (append ? this.virtualTempIDBottom++ : this.virtualTempIDTop--); element.dataset.virtual = '' + (append ? this.virtualTempIDBottom++ : this.virtualTempIDTop--);
@ -401,9 +367,29 @@ export default class Scrollable {
return !!element.parentElement; return !!element.parentElement;
} }
public scrollIntoView(element: Element) { public scrollIntoView(element: HTMLElement) {
if(element.parentElement) { if(element.parentElement) {
element.scrollIntoView(); let scrollTop = this.scrollTop;
let offsetTop = element.offsetTop;
let clientHeight = this.container.clientHeight;
let height = element.scrollHeight;
let diff = (clientHeight - height) / 2;
/* if(scrollTop < offsetTop) {
offsetTop += diff;
} else { */
offsetTop -= diff;
//}
if(this.scrollLocked) clearTimeout(this.scrollLocked);
this.scrollLocked = setTimeout(() => {
this.scrollLocked = 0;
this.onScroll();
}, 468);
this.container.scrollTo({behavior: 'smooth', top: offsetTop});
//element.scrollIntoView({behavior: 'smooth', block: 'center'});
} }
} }

View File

@ -875,22 +875,7 @@ export function wrapReply(title: string, subtitle: string, message?: any) {
let media = message && message.media; let media = message && message.media;
if(media) { if(media) {
if(message.grouped_id) { replySubtitle.innerHTML = message.rReply;
replySubtitle.innerHTML = 'Album';
} else if(media._ == 'messageMediaContact') {
replySubtitle.innerHTML = 'Contact';
} else if(media._ == 'messageMediaPoll') {
replySubtitle.innerHTML = media.poll.rReply;
} else if(media.photo) {
replySubtitle.innerHTML = 'Photo';
} else if(media.document && 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 {
replySubtitle.innerHTML = media._;
}
if(media.photo || (media.document && ['video'].indexOf(media.document.type) !== -1)) { if(media.photo || (media.document && ['video'].indexOf(media.document.type) !== -1)) {
let replyMedia = document.createElement('div'); let replyMedia = document.createElement('div');
@ -1054,8 +1039,9 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo
} }
} }
export function wrapPoll(poll: Poll, results: PollResults) { export function wrapPoll(pollID: string, mid: number) {
let elem = new PollElement(); let elem = new PollElement();
elem.setAttribute('poll-id', poll.id); elem.setAttribute('poll-id', pollID);
elem.setAttribute('message-id', '' + mid);
return elem; return elem;
} }

View File

@ -143,28 +143,41 @@ export class AppDialogsManager {
let dialog: any = e.detail; let dialog: any = e.detail;
this.setLastMessage(dialog); this.setLastMessage(dialog);
this.setUnreadMessages(dialog);
this.setDialogPosition(dialog); this.setDialogPosition(dialog);
this.setPinnedDelimiter();
}); });
$rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => { $rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => {
let dialogs = e.detail; let dialogs = e.detail;
let performed = 0;
for(let id in dialogs) { for(let id in dialogs) {
let dialog = dialogs[id]; let dialog = dialogs[id];
/////console.log('updating dialog:', dialog); /////console.log('updating dialog:', dialog);
++performed;
if(!(dialog.peerID in this.doms)) { if(!(dialog.peerID in this.doms)) {
this.addDialog(dialog); this.addDialog(dialog);
continue;
} }
this.setLastMessage(dialog); this.setLastMessage(dialog);
this.setUnreadMessages(dialog);
this.setDialogPosition(dialog); this.setDialogPosition(dialog);
} }
this.setPinnedDelimiter();
});
$rootScope.$on('dialog_drop', (e: CustomEvent) => {
let {peerID, dialog} = e.detail;
let dom = this.getDialogDom(peerID);
if(dom) {
dom.listEl.remove();
delete this.doms[peerID];
(dialog.folder_id == 1 ? this.scrollArchived : this.scroll).reorder();
}
}); });
$rootScope.$on('dialog_unread', (e: CustomEvent) => { $rootScope.$on('dialog_unread', (e: CustomEvent) => {
@ -184,6 +197,7 @@ export class AppDialogsManager {
}); });
this.loadDialogs().then(result => { this.loadDialogs().then(result => {
this.setPinnedDelimiter();
//appSidebarLeft.onChatsScroll(); //appSidebarLeft.onChatsScroll();
this.loadDialogs(true); this.loadDialogs(true);
}); });
@ -331,10 +345,11 @@ export class AppDialogsManager {
}); });
} }
public setDialogPosition(dialog: any) { public setDialogPosition(dialog: Dialog) {
let pos = appMessagesManager.getDialogByPeerID(dialog.peerID)[1]; let pos = appMessagesManager.getDialogByPeerID(dialog.peerID)[1];
let dom = this.getDialogDom(dialog.peerID); let dom = this.getDialogDom(dialog.peerID);
let prevPos = whichChild(dom.listEl); let prevPos = whichChild(dom.listEl);
if(prevPos == pos) { if(prevPos == pos) {
return; return;
} else if(prevPos < pos) { // was higher } else if(prevPos < pos) { // was higher
@ -348,103 +363,45 @@ export class AppDialogsManager {
chatList.append(dom.listEl); chatList.append(dom.listEl);
} }
// fix order (dialog.folder_id == 1 ? this.scrollArchived : this.scroll).reorder();
(Array.from(chatList.children) as HTMLElement[]).forEach((el, idx) => {
el.dataset.virtual = '' + idx;
});
this.log('setDialogPosition:', dialog, dom, pos); this.log('setDialogPosition:', dialog, dom, pos);
} }
/* public sortDom(archived = false) { public setPinnedDelimiter() {
//if(archived) return; let index = -1;
let dialogs = appMessagesManager.dialogsStorage.dialogs[0];
let dialogs = appMessagesManager.dialogsStorage.dialogs.slice(); for(let dialog of dialogs) {
if(dialog.pFlags?.pinned) {
let inUpper: Scrollable['hiddenElements']['up'] = []; index++;
let inBottom: Scrollable['hiddenElements']['down'] = []; }
let inVisible: Scrollable['visibleElements'] = [];
let pinnedDialogs = [];
let sorted = dialogs;
if(!archived) {
for(let i = 0; i < dialogs.length; ++i) {
let dialog = dialogs[i];
if(!dialog.pFlags.pinned) break;
pinnedDialogs.push(dialog);
} }
if(pinnedDialogs.length) { let currentIndex = (this.pinnedDelimiter.parentElement && whichChild(this.pinnedDelimiter.parentElement)) ?? -1;
let dom = this.getDialogDom(pinnedDialogs[pinnedDialogs.length - 1].peerID);
if(dom) { if(index == currentIndex) return;
dom.listEl.append(this.pinnedDelimiter);
let children = this.chatList.children;
let modifying: HTMLElement[] = [];
if(currentIndex != -1 && children.length > currentIndex) {
let li = children[currentIndex] as HTMLElement;
modifying.push(li);
} }
if(index != -1 && children.length > index) {
let li = children[index] as HTMLElement;
modifying.push(li);
li.append(this.pinnedDelimiter);
} else { } else {
if(this.pinnedDelimiter.parentElement) { this.pinnedDelimiter.remove();
this.pinnedDelimiter.parentElement.removeChild(this.pinnedDelimiter);
}
} }
sorted = sorted.filter((d: any) => !d.pFlags.pinned && d.folder_id != 1); modifying.forEach(elem => {
} else { this.scroll.updateElement(elem);
sorted = sorted.filter((d: any) => d.folder_id == 1);
}
sorted = sorted.sort((a: any, b: any) => {
let timeA = appMessagesManager.getMessage(a.top_message).date;
let timeB = appMessagesManager.getMessage(b.top_message).date;
return timeB - timeA;
}); });
if(!archived) {
sorted = pinnedDialogs.concat(sorted);
} }
//console.log('sortDom', sorted, this.chatsHidden, this.chatsHidden.up, this.chatsHidden.down);
let chatList = archived ? this.chatListArchived : this.chatList;
let chatsHidden = archived ? this.chatsArchivedHidden : this.chatsHidden;
let chatsVisible = archived ? this.chatsArchivedVisible : this.chatsVisible;
let hiddenLength: number = chatsHidden.up.length;
let inViewportLength = chatList.childElementCount;
let concated = chatsHidden.up.concat(chatsVisible, chatsHidden.down);
//console.log('sortDom clearing innerHTML', archived, hiddenLength, inViewportLength);
chatList.innerHTML = '';
let inViewportIndex = 0;
sorted.forEach((d: any, idx) => {
let dom = this.getDialogDom(d.peerID);
if(!dom) return;
let child = concated.find((obj: any) => obj.element == dom.listEl);
if(!child) {
return this.log.error('no child by listEl:', dom.listEl, archived, concated);
}
if(inUpper.length < hiddenLength) {
inUpper.push(child);
} else if(inViewportIndex <= inViewportLength - 1) {
chatList.append(dom.listEl);
inVisible.push(child);
++inViewportIndex;
} else {
inBottom.push(child);
}
});
//console.log('sortDom', sorted.length, inUpper.length, chatList.childElementCount, inBottom.length);
chatsHidden.up = inUpper;
chatsVisible.length = 0;
chatsVisible.push(...inVisible);
chatsHidden.down = inBottom;
} */
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) { public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) {
if(!lastMessage) { if(!lastMessage) {
lastMessage = appMessagesManager.getMessage(dialog.top_message); lastMessage = appMessagesManager.getMessage(dialog.top_message);
@ -465,94 +422,7 @@ export class AppDialogsManager {
//console.log('setting last message:', lastMessage); //console.log('setting last message:', lastMessage);
/* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ { /* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ {
let lastMessageText = ''; /* let messageText = lastMessage.message;
if(lastMessage.media) {
switch(lastMessage.media._) {
case 'messageMediaPhoto':
lastMessageText += '<i>' + (lastMessage.grouped_id ? 'Album' : 'Photo') + (lastMessage.message ? ', ' : '') + '</i>';
break;
case 'messageMediaGeo':
lastMessageText += '<i>Geolocation</i>';
break;
case 'messageMediaPoll':
lastMessageText += '<i>' + lastMessage.media.poll.rReply + '</i>';
break;
case 'messageMediaContact':
lastMessageText += '<i>Contact</i>';
break;
case 'messageMediaDocument':
let document = lastMessage.media.document;
let found = false;
for(let attribute of document.attributes) {
if(found) break;
switch(attribute._) {
case 'documentAttributeSticker':
lastMessageText += RichTextProcessor.wrapRichText(attribute.alt) + '<i>Sticker</i>';
found = true;
break;
case 'documentAttributeFilename':
lastMessageText += '<i>' + attribute.file_name + '</i>';
found = true;
break;
/* default:
console.warn('Got unknown document type!', lastMessage);
break; */
}
}
if(document.type == 'video') {
lastMessageText = '<i>Video' + (lastMessage.message ? ', ' : '') + '</i>';
found = true;
} else if(document.type == 'voice') {
lastMessageText = '<i>Voice message</i>';
found = true;
} else if(document.type == 'gif') {
lastMessageText = '<i>GIF' + (lastMessage.message ? ', ' : '') + '</i>';
found = true;
} else if(document.type == 'round') {
lastMessageText = '<i>Video message' + (lastMessage.message ? ', ' : '') + '</i>';
found = true;
}
if(found) {
break;
}
default:
///////console.warn('Got unknown lastMessage.media type!', lastMessage);
break;
}
}
if(lastMessage.action) {
let action = lastMessage.action;
console.log('lastMessage action:', action);
let suffix = '';
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
let duration = action.duration;
if(duration) {
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');
suffix = ' (' + d.reverse().join(' ') + ')';
}
}
// @ts-ignore
lastMessageText = '<i>' + langPack[_] + suffix + '</i>';
}
let messageText = lastMessage.message;
let messageWrapped = ''; let messageWrapped = '';
if(messageText) { if(messageText) {
let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '), {noLinebreakers: true}); let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '), {noLinebreakers: true});
@ -577,9 +447,10 @@ export class AppDialogsManager {
entities: entities, entities: entities,
noTextFormat: true noTextFormat: true
}); });
} } */
dom.lastMessageSpan.innerHTML = lastMessageText + messageWrapped; //dom.lastMessageSpan.innerHTML = lastMessageText + messageWrapped;
dom.lastMessageSpan.innerHTML = lastMessage.rReply;
/* if(lastMessage.from_id == auth.id) { // You: */ /* if(lastMessage.from_id == auth.id) { // You: */
if(peer._ != 'peerUser' && peerID != -lastMessage.from_id) { if(peer._ != 'peerUser' && peerID != -lastMessage.from_id) {
@ -629,10 +500,9 @@ export class AppDialogsManager {
} }
} }
public setUnreadMessages(dialog: any) { public setUnreadMessages(dialog: Dialog) {
let dom = this.getDialogDom(dialog.peerID); let dom = this.getDialogDom(dialog.peerID);
dom.statusSpan.innerHTML = '';
let lastMessage = appMessagesManager.getMessage(dialog.top_message); let lastMessage = appMessagesManager.getMessage(dialog.top_message);
if(lastMessage._ != 'messageEmpty' && if(lastMessage._ != 'messageEmpty' &&
lastMessage.from_id == $rootScope.myID && lastMessage.peerID != $rootScope.myID && lastMessage.from_id == $rootScope.myID && lastMessage.peerID != $rootScope.myID &&
@ -651,10 +521,11 @@ export class AppDialogsManager {
} }
} else dom.statusSpan.classList.remove('tgico-check', 'tgico-checks'); } else dom.statusSpan.classList.remove('tgico-check', 'tgico-checks');
dom.unreadMessagesSpan.innerHTML = ''; dom.unreadMessagesSpan.innerText = '';
if(dialog.unread_count) {
dom.unreadMessagesSpan.innerHTML = dialog.unread_count;
dom.unreadMessagesSpan.classList.remove('tgico-pinnedchat'); dom.unreadMessagesSpan.classList.remove('tgico-pinnedchat');
if(dialog.unread_count) {
dom.unreadMessagesSpan.innerText = '' + dialog.unread_count;
//dom.unreadMessagesSpan.classList.remove('tgico-pinnedchat');
dom.unreadMessagesSpan.classList.add(new Date(dialog.notify_settings.mute_until * 1000) > new Date() ? dom.unreadMessagesSpan.classList.add(new Date(dialog.notify_settings.mute_until * 1000) > new Date() ?
'unread-muted' : 'unread'); 'unread-muted' : 'unread');
} else if(dialog.pFlags.pinned) { } else if(dialog.pFlags.pinned) {
@ -826,12 +697,6 @@ export class AppDialogsManager {
this.doms[dialog.peerID] = dom; this.doms[dialog.peerID] = dom;
} }
if(dialog.pFlags.pinned) {
li.classList.add('dialog-pinned');
//this.chatList.insertBefore(this.pinnedDelimiter, li.nextSibling);
dom.listEl.append(this.pinnedDelimiter);
}
this.setLastMessage(dialog); this.setLastMessage(dialog);
} else { } else {
container.append(li); container.append(li);

View File

@ -274,7 +274,7 @@ export class AppImManager {
/////this.log('setting pinned message', message); /////this.log('setting pinned message', message);
this.pinnedMessageContainer.dataset.mid = '' + mid; this.pinnedMessageContainer.dataset.mid = '' + mid;
this.pinnedMessageContainer.style.display = ''; this.pinnedMessageContainer.style.display = '';
this.pinnedMessageContent.innerHTML = RichTextProcessor.wrapEmojiText(message.message); this.pinnedMessageContent.innerHTML = message.rReply;
} }
this.needUpdate.forEachReverse((obj, idx) => { this.needUpdate.forEachReverse((obj, idx) => {
@ -329,11 +329,17 @@ export class AppImManager {
let target = e.target as HTMLElement; let target = e.target as HTMLElement;
let bubble: HTMLDivElement = null; let bubble: HTMLDivElement = null;
try { try {
bubble = findUpClassName(e.target, 'bubble'); bubble = findUpClassName(target, 'bubble');
} catch(err) {} } catch(err) {}
if(!bubble) return; if(!bubble) return;
let contactDiv = findUpClassName(target, 'contact');
if(contactDiv) {
this.setPeer(+contactDiv.dataset.peerID);
return;
}
//this.log('chatInner click:', target); //this.log('chatInner click:', target);
if(target.tagName == 'SPAN') { if(target.tagName == 'SPAN') {
(target.parentElement.querySelector('video') as HTMLElement).click(); // hot-fix for time and play button (target.parentElement.querySelector('video') as HTMLElement).click(); // hot-fix for time and play button
@ -859,7 +865,7 @@ export class AppImManager {
appMessagesManager.wrapSingleMessage(chatInfo.pinned_msg_id); appMessagesManager.wrapSingleMessage(chatInfo.pinned_msg_id);
} }
let participants_count = chatInfo.participants_count || chatInfo.participants.participants.length; let participants_count = chatInfo.participants_count || (chatInfo.participants && chatInfo.participants.participants.length);
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members'); let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
if(onlines > 1) { if(onlines > 1) {
@ -1091,9 +1097,9 @@ export class AppImManager {
return true; return true;
})/* .catch(err => { })/* .catch(err => {
this.log.error(err); this.log.error(err);
}) *//* , }) */,
appSidebarRight.fillProfileElements() *//* , appSidebarRight.fillProfileElements()/* ,
appSidebarRight.loadSidebarMedia(true) */ appSidebarRight.loadSidebarMedia(true) */
]).catch(err => { ]).catch(err => {
this.log.error('setPeer promises error:', err); this.log.error('setPeer promises error:', err);
@ -1716,6 +1722,8 @@ export class AppImManager {
let contactDiv = document.createElement('div'); let contactDiv = document.createElement('div');
contactDiv.classList.add('contact'); contactDiv.classList.add('contact');
contactDiv.dataset.peerID = '' + message.media.user_id;
messageDiv.classList.add('contact-message'); messageDiv.classList.add('contact-message');
processingWebPage = true; processingWebPage = true;
@ -1724,12 +1732,16 @@ export class AppImManager {
if(message.media.last_name) texts.push(RichTextProcessor.wrapEmojiText(message.media.last_name)); if(message.media.last_name) texts.push(RichTextProcessor.wrapEmojiText(message.media.last_name));
contactDiv.innerHTML = ` contactDiv.innerHTML = `
<div class="contact-avatar user-avatar"><img src="blob:https://192.168.0.105:9000/803514b4-4a46-4125-984f-ca8f86405ef2"></div>
<div class="contact-details"> <div class="contact-details">
<div class="contact-name">${texts.join(' ')}</div> <div class="contact-name">${texts.join(' ')}</div>
<div class="contact-number">${message.media.phone_number ? '+' + formatPhoneNumber(message.media.phone_number).formatted : 'Unknown phone number'}</div> <div class="contact-number">${message.media.phone_number ? '+' + formatPhoneNumber(message.media.phone_number).formatted : 'Unknown phone number'}</div>
</div>`; </div>`;
let avatarDiv = document.createElement('div');
avatarDiv.classList.add('contact-avatar', 'user-avatar');
contactDiv.prepend(avatarDiv);
appProfileManager.putPhoto(avatarDiv, message.media.user_id);
bubble.classList.remove('is-message-empty'); bubble.classList.remove('is-message-empty');
messageDiv.append(contactDiv); messageDiv.append(contactDiv);
@ -1739,7 +1751,7 @@ export class AppImManager {
case 'messageMediaPoll': { case 'messageMediaPoll': {
bubble.classList.remove('is-message-empty'); bubble.classList.remove('is-message-empty');
let pollElement = wrapPoll(message.media.poll, message.media.results); let pollElement = wrapPoll(message.media.poll.id, message.mid);
messageDiv.prepend(pollElement); messageDiv.prepend(pollElement);
break; break;
@ -1927,11 +1939,14 @@ export class AppImManager {
bubbles.push(bubble); bubbles.push(bubble);
}); */ }); */
//let leftHeightToScroll = this.scrollable.innerHeight; let scrollTop = this.scrollable.scrollTop;
let leftHeightToScroll = scrollTop > 0 ? 0 : (Object.keys(this.bubbles).length > 0 ? 1 : this.scrollable.container.clientHeight);
let setScrollRAF = 0;
let previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop;
//console.timeEnd('appImManager: pre render start'); //console.timeEnd('appImManager: pre render start');
//this.log('start performHistoryResult, scrollTop:', this.scrollable.scrollTop, this.scrollable.scrollHeight, this.scrollable.innerHeight); this.log('start performHistoryResult, scrollTop:', scrollTop, leftHeightToScroll);
let method = (reverse ? history.shift : history.pop).bind(history); let method = (reverse ? history.shift : history.pop).bind(history);
@ -1981,21 +1996,27 @@ export class AppImManager {
} */ } */
if(!renderedFirstScreen) { if(!renderedFirstScreen) {
if(!this.scrollable.scrollTop) { /* if(!this.scrollable.scrollTop) {
let height = bubble.scrollHeight; let height = bubble.scrollHeight;
//let height = Math.ceil(bubble.getBoundingClientRect().height); //let height = Math.ceil(bubble.getBoundingClientRect().height);
this.scrollable.scrollTop += height; this.scrollable.scrollTop += height;
//innerHeight -= height; //innerHeight -= height;
} } */
/* if(leftHeightToScroll >= 0) { /* if(leftHeightToScroll > 0) {
let height = bubble.scrollHeight; let height = bubble.scrollHeight;
leftHeightToScroll -= height; leftHeightToScroll -= height;
this.scrollable.scrollTop += height;
} */ else { if(setScrollRAF) window.cancelAnimationFrame(setScrollRAF);
setScrollRAF = window.requestAnimationFrame(() => {
//this.scrollable.scrollTop += height;
this.scrollable.scrollTop = this.scrollable.scrollHeight - previousScrollHeightMinusTop;
});
//this.scrollable.scrollTop += height;
} else { */
renderedFirstScreen = true; renderedFirstScreen = true;
resolve(); resolve();
resolved = true; resolved = true;
} //}
} }
} else { } else {
////////this.scrollable.append(bubble); ////////this.scrollable.append(bubble);
@ -2026,6 +2047,14 @@ export class AppImManager {
}; };
firstLoad ? window.requestAnimationFrame(r) : r(); firstLoad ? window.requestAnimationFrame(r) : r();
if(reverse) {
//if(setScrollRAF) window.cancelAnimationFrame(setScrollRAF);
//setScrollRAF = window.requestAnimationFrame(() => {
//this.scrollable.scrollTop += height;
this.scrollable.scrollTop = this.scrollable.scrollHeight - previousScrollHeightMinusTop;
//});
}
//r(); //r();
/* method((msgID) => { /* method((msgID) => {
let message = appMessagesManager.getMessage(msgID); let message = appMessagesManager.getMessage(msgID);
@ -2054,7 +2083,7 @@ export class AppImManager {
let pageCount = this.bubblesContainer.clientHeight / 38/* * 1.25 */ | 0; let pageCount = this.bubblesContainer.clientHeight / 38/* * 1.25 */ | 0;
//let loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount; //let loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount;
let realLoadCount = Object.keys(this.bubbles).length > 0 ? 40 : pageCount;//let realLoadCount = 50; let realLoadCount = Object.keys(this.bubbles).length > 0 ? Math.max(40, pageCount) : pageCount;//let realLoadCount = 50;
let loadCount = realLoadCount; let loadCount = realLoadCount;
if(testScroll) { if(testScroll) {
@ -2122,10 +2151,9 @@ export class AppImManager {
ids = Object.keys(this.bubbles).map(i => +i).sort((a, b) => a - b); ids = Object.keys(this.bubbles).map(i => +i).sort((a, b) => a - b);
} }
this.log('getHistory: slice loadedTimes:', reverse, pageCount, this.loadedTopTimes, this.loadedBottomTimes, ids && ids.length);
//let removeCount = loadCount / 2; //let removeCount = loadCount / 2;
let safeCount = Math.min(realLoadCount * 2, 35); // cause i've been runningrunningrunning all day let safeCount = realLoadCount * 2; // cause i've been runningrunningrunning all day
this.log('getHistory: slice loadedTimes:', reverse, pageCount, this.loadedTopTimes, this.loadedBottomTimes, ids && ids.length, safeCount);
if(ids && ids.length > safeCount) { if(ids && ids.length > safeCount) {
if(reverse) { if(reverse) {
//ids = ids.slice(-removeCount); //ids = ids.slice(-removeCount);

View File

@ -1,4 +1,4 @@
import { SearchIndexManager, $rootScope, copy, tsNow, safeReplaceObject, dT, _, listMergeSorted, deepEqual } from "../utils"; import { SearchIndexManager, $rootScope, copy, tsNow, safeReplaceObject, dT, _, listMergeSorted, deepEqual, langPack } from "../utils";
import appMessagesIDsManager from "./appMessagesIDsManager"; import appMessagesIDsManager from "./appMessagesIDsManager";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
@ -106,15 +106,13 @@ export class AppMessagesManager {
public pinnedIndex = 0; public pinnedIndex = 0;
public dialogsNum = 0; public dialogsNum = 0;
public migratedFromTo: any = {}; public migratedFromTo: {[peerID: number]: number} = {};
public migratedToFrom: any = {}; public migratedToFrom: {[peerID: number]: number} = {};
public newMessagesHandlePromise = 0; public newMessagesHandlePromise = 0;
public newMessagesToHandle: any = {}; public newMessagesToHandle: any = {};
public newDialogsHandlePromise = 0; public newDialogsHandlePromise = 0;
public newDialogsToHandle: any = {}; public newDialogsToHandle: {[peerID: string]: {reload: true} | Dialog} = {};
//public notificationsHandlePromise = 0;
//public notificationsToHandle: any = {};
public newUpdatesAfterReloadToHandle: any = {}; public newUpdatesAfterReloadToHandle: any = {};
public fwdMessagesPluralize = _('conversation_forwarded_X_messages'); public fwdMessagesPluralize = _('conversation_forwarded_X_messages');
@ -125,7 +123,7 @@ export class AppMessagesManager {
if(maxID && !appMessagesIDsManager.getMessageIDInfo(maxID)[1]) { if(maxID && !appMessagesIDsManager.getMessageIDInfo(maxID)[1]) {
this.maxSeenID = maxID; this.maxSeenID = maxID;
} }
}) });
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update: any = e.detail; let update: any = e.detail;
@ -183,7 +181,7 @@ export class AppMessagesManager {
index: dialog.index index: dialog.index
}); });
} }
}) });
} }
public getInputEntities(entities: any) { public getInputEntities(entities: any) {
@ -248,7 +246,7 @@ export class AppMessagesManager {
entities: this.getInputEntities(entities), entities: this.getInputEntities(entities),
no_webpage: noWebPage, no_webpage: noWebPage,
}).then((updates) => { }).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates) apiUpdatesManager.processUpdateMessage(updates);
}, (error) => { }, (error) => {
if(error && error.type == 'MESSAGE_NOT_MODIFIED') { if(error && error.type == 'MESSAGE_NOT_MODIFIED') {
error.handled = true; error.handled = true;
@ -1129,20 +1127,6 @@ export class AppMessagesManager {
return false; return false;
} }
public async getConversation(peerID: number) {
var foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog.length) {
return foundDialog[0];
}
return {
peerID: peerID,
top_message: 0,
index: this.generateDialogIndex(this.generateDialogPinnedDate()),
pFlags: {}
};
}
public getConversations(offsetIndex?: number, limit = 20, folderID = 0) { public getConversations(offsetIndex?: number, limit = 20, folderID = 0) {
let curDialogStorage = this.dialogsStorage.dialogs[folderID] ?? (this.dialogsStorage.dialogs[folderID] = []); let curDialogStorage = this.dialogsStorage.dialogs[folderID] ?? (this.dialogsStorage.dialogs[folderID] = []);
@ -1327,13 +1311,47 @@ export class AppMessagesManager {
return Promise.all(promises); return Promise.all(promises);
} }
/*
var date = Date.now() / 1000 | 0;
var m = date * 0x10000;
var k = (date + 1) * 0x10000;
k - m;
65536
*/
public generateDialogIndex(date?: any) { public generateDialogIndex(date?: any) {
if(date === undefined) { if(date === undefined) {
date = tsNow(true) + serverTimeManager.serverTimeOffset; date = tsNow(true) + serverTimeManager.serverTimeOffset;
} }
return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF); return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);
} }
public generateIndexForDialog(dialog: Dialog) {
let channelID = AppPeersManager.isChannel(dialog.peerID) ? -dialog.peerID : 0;
let mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
let message = this.getMessage(mid);
let topDate = message.date;
if(channelID) {
let channel = appChatsManager.getChat(channelID);
if(!topDate || channel.date && channel.date > topDate) {
topDate = channel.date;
}
}
let savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
if(savedDraft && savedDraft.date > topDate) {
topDate = savedDraft.date;
}
if(dialog.pFlags.pinned) {
topDate = this.generateDialogPinnedDate(dialog);
//console.log('topDate', peerID, topDate);
}
dialog.index = this.generateDialogIndex(topDate);
}
public pushDialogToStorage(dialog: Dialog, offsetDate?: number) { public pushDialogToStorage(dialog: Dialog, offsetDate?: number) {
let dialogs = this.dialogsStorage.dialogs[dialog.folder_id] ?? (this.dialogsStorage.dialogs[dialog.folder_id] = []); let dialogs = this.dialogsStorage.dialogs[dialog.folder_id] ?? (this.dialogsStorage.dialogs[dialog.folder_id] = []);
let pos = this.getDialogByPeerID(dialog.peerID)[1]; let pos = this.getDialogByPeerID(dialog.peerID)[1];
@ -1504,7 +1522,7 @@ export class AppMessagesManager {
} }
break; break;
case 'messageMediaPoll': case 'messageMediaPoll':
appPollsManager.savePoll(apiMessage.media.poll, apiMessage.media.results); apiMessage.media.poll = appPollsManager.savePoll(apiMessage.media.poll, apiMessage.media.results);
break; break;
case 'messageMediaDocument': case 'messageMediaDocument':
if(apiMessage.media.ttl_seconds) { if(apiMessage.media.ttl_seconds) {
@ -1512,6 +1530,8 @@ export class AppMessagesManager {
} else { } else {
apiMessage.media.document = appDocsManager.saveDoc(apiMessage.media.document, mediaContext); // 11.04.2020 warning apiMessage.media.document = appDocsManager.saveDoc(apiMessage.media.document, mediaContext); // 11.04.2020 warning
} }
break; break;
case 'messageMediaWebPage': case 'messageMediaWebPage':
/* if(apiMessage.media.webpage.document) { /* if(apiMessage.media.webpage.document) {
@ -1615,6 +1635,8 @@ export class AppMessagesManager {
} }
} }
apiMessage.rReply = this.getRichReplyText(apiMessage);
if(apiMessage.message && apiMessage.message.length) { if(apiMessage.message && apiMessage.message.length) {
var myEntities = RichTextProcessor.parseEntities(apiMessage.message); var myEntities = RichTextProcessor.parseEntities(apiMessage.message);
var apiEntities = apiMessage.entities || []; var apiEntities = apiMessage.entities || [];
@ -1626,7 +1648,110 @@ export class AppMessagesManager {
if(!options.isEdited) { if(!options.isEdited) {
this.messagesStorage[mid] = apiMessage; this.messagesStorage[mid] = apiMessage;
} }
}) });
}
public getRichReplyText(message: any) {
let messageText = '';
if(message.media) {
switch(message.media._) {
case 'messageMediaPhoto':
messageText += '<i>' + (message.grouped_id ? 'Album' : 'Photo') + (message.message ? ', ' : '') + '</i>';
break;
case 'messageMediaGeo':
messageText += '<i>Geolocation</i>';
break;
case 'messageMediaPoll':
messageText += '<i>' + message.media.poll.rReply + '</i>';
break;
case 'messageMediaContact':
messageText += '<i>Contact</i>';
break;
case 'messageMediaDocument':
let document = message.media.document;
let found = false;
for(let attribute of document.attributes) {
if(found) break;
switch(attribute._) {
case 'documentAttributeSticker':
messageText += RichTextProcessor.wrapRichText(attribute.alt) + '<i>Sticker</i>';
found = true;
break;
case 'documentAttributeFilename':
messageText += '<i>' + attribute.file_name + '</i>';
found = true;
break;
/* default:
console.warn('Got unknown document type!', message);
break; */
}
}
if(document.type == 'video') {
messageText = '<i>Video' + (message.message ? ', ' : '') + '</i>';
found = true;
} else if(document.type == 'voice') {
messageText = '<i>Voice message</i>';
found = true;
} else if(document.type == 'gif') {
messageText = '<i>GIF' + (message.message ? ', ' : '') + '</i>';
found = true;
} else if(document.type == 'round') {
messageText = '<i>Video message' + (message.message ? ', ' : '') + '</i>';
found = true;
}
if(found) {
break;
}
default:
///////console.warn('Got unknown message.media type!', message);
break;
}
}
if(message.action) {
let action = message.action;
console.log('message action:', action);
let suffix = '';
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
let duration = action.duration;
if(duration) {
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');
suffix = ' (' + d.reverse().join(' ') + ')';
}
}
// @ts-ignore
messageText = '<i>' + langPack[_] + suffix + '</i>';
}
let text = message.message;
let messageWrapped = '';
if(text) {
let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true});
messageWrapped = RichTextProcessor.wrapRichText(text, {
noLinebreakers: true,
entities: entities,
noTextFormat: true
});
}
return messageText + messageWrapped;
} }
public migrateChecks(migrateFrom: number, migrateTo: number) { public migrateChecks(migrateFrom: number, migrateTo: number) {
@ -1644,7 +1769,7 @@ export class AppMessagesManager {
var foundDialog = this.getDialogByPeerID(migrateFrom); var foundDialog = this.getDialogByPeerID(migrateFrom);
if(foundDialog.length) { if(foundDialog.length) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1); this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: migrateFrom}); $rootScope.$broadcast('dialog_drop', {peerID: migrateFrom, dialog: foundDialog[0]});
} }
$rootScope.$broadcast('dialog_migrate', {migrateFrom: migrateFrom, migrateTo: migrateTo}); $rootScope.$broadcast('dialog_migrate', {migrateFrom: migrateFrom, migrateTo: migrateTo});
@ -1703,7 +1828,7 @@ export class AppMessagesManager {
//console.log('applyConversation', dialogsResult); //console.log('applyConversation', dialogsResult);
var updatedDialogs: any = {}; var updatedDialogs: {[peerID: number]: Dialog} = {};
var hasUpdated = false; var hasUpdated = false;
dialogsResult.dialogs.forEach((dialog: any) => { dialogsResult.dialogs.forEach((dialog: any) => {
var peerID = AppPeersManager.getPeerID(dialog.peer); var peerID = AppPeersManager.getPeerID(dialog.peer);
@ -1738,7 +1863,7 @@ export class AppMessagesManager {
var foundDialog = this.getDialogByPeerID(peerID); var foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog.length) { if(foundDialog.length) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1); this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: peerID}); $rootScope.$broadcast('dialog_drop', {peerID: peerID, dialog: foundDialog[0]});
} }
} }
@ -1806,24 +1931,7 @@ export class AppMessagesManager {
dialog.read_inbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_inbox_max_id, channelID); dialog.read_inbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_inbox_max_id, channelID);
dialog.read_outbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_outbox_max_id, channelID); dialog.read_outbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_outbox_max_id, channelID);
var topDate = message.date; this.generateIndexForDialog(dialog);
if(channelID) {
var channel = appChatsManager.getChat(channelID);
if(!topDate || channel.date && channel.date > topDate) {
topDate = channel.date;
}
}
var savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
if(savedDraft && savedDraft.date > topDate) {
topDate = savedDraft.date;
}
if(dialog.pFlags.pinned) {
topDate = this.generateDialogPinnedDate(dialog);
//console.log('topDate', peerID, topDate);
}
dialog.index = this.generateDialogIndex(topDate);
dialog.peerID = peerID; dialog.peerID = peerID;
if(!dialog.folder_id) dialog.folder_id = 0; if(!dialog.folder_id) dialog.folder_id = 0;
@ -2146,7 +2254,7 @@ export class AppMessagesManager {
}); });
} }
public generateDialogPinnedDate(dialog?: any) { public generateDialogPinnedDate(dialog?: Dialog) {
let pinnedIndex: number; let pinnedIndex: number;
if(dialog) { if(dialog) {
@ -2174,10 +2282,10 @@ export class AppMessagesManager {
clearTimeout(this.newDialogsHandlePromise); clearTimeout(this.newDialogsHandlePromise);
this.newDialogsHandlePromise = 0; this.newDialogsHandlePromise = 0;
var newMaxSeenID = 0 let newMaxSeenID = 0;
Object.keys(this.newDialogsToHandle).forEach((peerID) => { Object.keys(this.newDialogsToHandle).forEach((peerID) => {
let dialog = this.newDialogsToHandle[peerID]; let dialog = this.newDialogsToHandle[peerID];
if(dialog.reload) { if('reload' in dialog) {
this.reloadConversation(+peerID); this.reloadConversation(+peerID);
delete this.newDialogsToHandle[peerID]; delete this.newDialogsToHandle[peerID];
} else { } else {
@ -2186,7 +2294,9 @@ export class AppMessagesManager {
newMaxSeenID = Math.max(newMaxSeenID, dialog.top_message || 0); newMaxSeenID = Math.max(newMaxSeenID, dialog.top_message || 0);
} }
} }
}) });
console.log('after order:', this.dialogsStorage.dialogs[0].map(d => d.peerID));
if(newMaxSeenID != 0) { if(newMaxSeenID != 0) {
this.incrementMaxSeenID(newMaxSeenID); this.incrementMaxSeenID(newMaxSeenID);
@ -2339,7 +2449,7 @@ export class AppMessagesManager {
} }
public handleUpdate(update: any) { public handleUpdate(update: any) {
//console.log('AMM: handleUpdate:', update._); console.log('AMM: handleUpdate:', update._);
switch(update._) { switch(update._) {
case 'updateMessageID': { case 'updateMessageID': {
var randomID = update.random_id; var randomID = update.random_id;
@ -2469,26 +2579,37 @@ export class AppMessagesManager {
break; break;
} }
/* case 'updateDialogPinned': { case 'updateDialogPinned': {
var peerID = AppPeersManager.getPeerID(update.peer); console.log('updateDialogPinned', update);
var foundDialog = this.getDialogByPeerID(peerID); let peerID = AppPeersManager.getPeerID(update.peer.peer);
let foundDialog = this.getDialogByPeerID(peerID);
if(!foundDialog.length || !update.pFlags.pinned) {
this.newDialogsToHandle[peerID] = {reload: true};
if(!this.newDialogsHandlePromise) { if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0); this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
} }
if(!foundDialog.length) {
this.newDialogsToHandle[peerID] = {reload: true};
break; break;
} else {
let dialog = foundDialog[0];
this.newDialogsToHandle[peerID] = dialog;
if(!update.pFlags.pinned) {
delete dialog.pFlags.pinned;
} else { // means set
dialog.pFlags.pinned = true;
}
this.generateIndexForDialog(dialog);
} }
var dialog = foundDialog[0];
dialog.index = this.generateDialogIndex(this.generateDialogPinnedDate(dialog));
dialog.pFlags.pinned = true;
break; break;
} }
case 'updatePinnedDialogs': { case 'updatePinnedDialogs': {
var newPinned: any = {}; console.log('updatePinnedDialogs', update);
let newPinned: {[peerID: number]: true} = {};
if(!update.order) { if(!update.order) {
apiManager.invokeApi('messages.getPinnedDialogs', {}).then((dialogsResult: any) => { apiManager.invokeApi('messages.getPinnedDialogs', {}).then((dialogsResult: any) => {
dialogsResult.dialogs.reverse(); dialogsResult.dialogs.reverse();
@ -2498,8 +2619,8 @@ export class AppMessagesManager {
newPinned[dialog.peerID] = true; newPinned[dialog.peerID] = true;
}); });
this.dialogsStorage.dialogs.forEach((dialog: any) => { this.dialogsStorage.dialogs[0].forEach((dialog: any) => {
var peerID = dialog.peerID; let peerID = dialog.peerID;
if(dialog.pFlags.pinned && !newPinned[peerID]) { if(dialog.pFlags.pinned && !newPinned[peerID]) {
this.newDialogsToHandle[peerID] = {reload: true}; this.newDialogsToHandle[peerID] = {reload: true};
if(!this.newDialogsHandlePromise) { if(!this.newDialogsHandlePromise) {
@ -2511,42 +2632,45 @@ export class AppMessagesManager {
break; break;
} }
update.order.reverse(); console.log('before order:', this.dialogsStorage.dialogs[0].map(d => d.peerID));
this.pinnedIndex = 0;
let willHandle = false;
update.order.reverse(); // index must be higher
update.order.forEach((peer: any) => { update.order.forEach((peer: any) => {
var peerID = AppPeersManager.getPeerID(peer); let peerID = AppPeersManager.getPeerID(peer.peer);
newPinned[peerID] = true; newPinned[peerID] = true;
var foundDialog = this.getDialogByPeerID(peerID); let foundDialog = this.getDialogByPeerID(peerID);
if(!foundDialog.length) { if(!foundDialog.length) {
this.newDialogsToHandle[peerID] = {reload: true} this.newDialogsToHandle[peerID] = {reload: true};
if(!this.newDialogsHandlePromise) { willHandle = true;
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
return; return;
} }
var dialog = foundDialog[0] let dialog = foundDialog[0];
dialog.index = this.generateDialogIndex(this.generateDialogPinnedDate(dialog)); delete dialog.pinnedIndex;
dialog.pFlags.pinned = true; dialog.pFlags.pinned = true;
this.generateIndexForDialog(dialog);
this.newDialogsToHandle[peerID] = dialog this.newDialogsToHandle[peerID] = dialog;
if(!this.newDialogsHandlePromise) { willHandle = true;
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0); });
}
})
this.dialogsStorage.dialogs.forEach((dialog: any) => { this.dialogsStorage.dialogs[0].forEach(dialog => {
var peerID = dialog.peerID; let peerID = dialog.peerID;
if(dialog.pFlags.pinned && !newPinned[peerID]) { if(dialog.pFlags.pinned && !newPinned[peerID]) {
this.newDialogsToHandle[peerID] = {reload: true} this.newDialogsToHandle[peerID] = {reload: true};
if(!this.newDialogsHandlePromise) { willHandle = true;
}
});
if(willHandle && !this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0); this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
} }
}
})
break; break;
} */ }
case 'updateEditMessage': case 'updateEditMessage':
case 'updateEditChannelMessage': { case 'updateEditChannelMessage': {
@ -2819,7 +2943,7 @@ export class AppMessagesManager {
} else { } else {
if(foundDialog[0]) { if(foundDialog[0]) {
this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1); this.dialogsStorage.dialogs[foundDialog[0].folder_id].splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: peerID}); $rootScope.$broadcast('dialog_drop', {peerID: peerID, dialog: foundDialog[0]});
} }
} }
} }
@ -2964,25 +3088,16 @@ export class AppMessagesManager {
}); });
} }
public getHistory(peerID: number, maxID = 0, limit = 0, backLimit?: number, prerendered?: number) { public getHistory(peerID: number, maxID = 0, limit: number, backLimit?: number) {
if(this.migratedFromTo[peerID]) { if(this.migratedFromTo[peerID]) {
peerID = this.migratedFromTo[peerID]; peerID = this.migratedFromTo[peerID];
} }
var historyStorage = this.historiesStorage[peerID]; var historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {count: null, history: [], pending: []});
var offset = 0; var offset = 0;
var offsetNotFound = false; var offsetNotFound = false;
var unreadOffset = 0; var unreadOffset = 0;
var unreadSkip = false; var unreadSkip = false;
prerendered = prerendered ? Math.min(50, prerendered) : 0;
if(historyStorage === undefined) {
historyStorage = this.historiesStorage[peerID] = {count: null, history: [], pending: []};
}
if(maxID < 0) {
maxID = 0;
}
var isMigrated = false; var isMigrated = false;
var reqPeerID = peerID; var reqPeerID = peerID;
if(this.migratedToFrom[peerID]) { if(this.migratedToFrom[peerID]) {
@ -2992,30 +3107,6 @@ export class AppMessagesManager {
} }
} }
if(!limit && !maxID) {
var foundDialog = this.getDialogByPeerID(peerID)[0];
if(foundDialog && foundDialog.unread_count > 1) {
var unreadCount = foundDialog.unread_count;
if(unreadSkip = (unreadCount > 50)) {
if(foundDialog.read_inbox_max_id) {
maxID = foundDialog.read_inbox_max_id;
backLimit = 16;
unreadOffset = 16;
limit = 4;
} else {
limit = 20;
unreadOffset = 16;
offset = unreadCount - unreadOffset;
}
} else {
limit = Math.max(10, prerendered, unreadCount + 2);
unreadOffset = unreadCount;
}
}/* else if('Mobile' in Config) {
limit = 20;
} */
}
if(maxID > 0) { if(maxID > 0) {
offsetNotFound = true; offsetNotFound = true;
for(offset = 0; offset < historyStorage.history.length; offset++) { for(offset = 0; offset < historyStorage.history.length; offset++) {
@ -3035,7 +3126,7 @@ export class AppMessagesManager {
offset = Math.max(0, offset - backLimit); offset = Math.max(0, offset - backLimit);
limit += backLimit; limit += backLimit;
} else { } else {
limit = limit || (offset ? 20 : (prerendered || 5)); limit = limit;
} }
var history = historyStorage.history.slice(offset, offset + limit); var history = historyStorage.history.slice(offset, offset + limit);
@ -3051,9 +3142,6 @@ export class AppMessagesManager {
}); });
} }
if(!backLimit && !limit) {
limit = prerendered || 20;
}
if(offsetNotFound) { if(offsetNotFound) {
offset = 0; offset = 0;
} }

View File

@ -1,4 +1,9 @@
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
import appMessagesManager from './appMessagesManager';
import appPeersManager from './appPeersManager';
import apiManager from "../mtproto/mtprotoworker";
import apiUpdatesManager from "./apiUpdatesManager";
import { $rootScope } from "../utils";
export type PollAnswer = { export type PollAnswer = {
_: 'pollAnswer', _: 'pollAnswer',
@ -58,24 +63,60 @@ export type Poll = {
}>, }>,
rQuestion?: string, rQuestion?: string,
rReply?: string, rReply?: string,
chosenIndex?: number
}; };
class AppPollsManager { class AppPollsManager {
private polls: {[id: string]: Poll} = {}; private polls: {[id: string]: Poll} = {};
private results: {[id: string]: PollResults} = {}; private results: {[id: string]: PollResults} = {};
constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update = e.detail;
this.handleUpdate(update);
});
}
public handleUpdate(update: any) {
switch(update._) {
case 'updateMessagePoll': { // when someone voted, we too
console.log('updateMessagePoll:', update);
let poll: Poll = this.polls[update.poll_id] || update.poll;
if(!poll) {
break;
}
poll = this.savePoll(poll, update.results);
$rootScope.$broadcast('poll_update', {poll, results: update.results});
break;
}
default:
break;
}
}
public savePoll(poll: Poll, results: PollResults) { public savePoll(poll: Poll, results: PollResults) {
let id = poll.id; let id = poll.id;
if(this.polls[id]) { if(this.polls[id]) {
this.results[id] = results; poll = this.polls[id];
return; this.saveResults(poll, results);
return poll;
} }
this.polls[id] = poll; this.polls[id] = poll;
this.results[id] = results;
poll.rQuestion = RichTextProcessor.wrapEmojiText(poll.question); poll.rQuestion = RichTextProcessor.wrapEmojiText(poll.question);
poll.rReply = RichTextProcessor.wrapEmojiText('📊') + ' ' + (poll.rQuestion || 'poll'); poll.rReply = RichTextProcessor.wrapEmojiText('📊') + ' ' + (poll.rQuestion || 'poll');
this.saveResults(poll, results);
return poll;
}
public saveResults(poll: Poll, results: PollResults) {
this.results[poll.id] = results;
poll.chosenIndex = (results && results.results && results.results.findIndex(answer => answer.pFlags?.chosen)) ?? -1;
} }
public getPoll(pollID: string): {poll: Poll, results: PollResults} { public getPoll(pollID: string): {poll: Poll, results: PollResults} {
@ -84,6 +125,27 @@ class AppPollsManager {
results: this.results[pollID] results: this.results[pollID]
}; };
} }
public sendVote(mid: number, optionIDs: number[]) {
let message = appMessagesManager.getMessage(mid);
let poll: Poll = message.media.poll;
let options: Uint8Array[] = optionIDs.map(index => {
return poll.answers[index].option;
});
let inputPeer = appPeersManager.getInputPeerByID(message.peerID);
let messageID = message.id;
return apiManager.invokeApi('messages.sendVote', {
peer: inputPeer,
msg_id: messageID,
options
}).then(updates => {
console.log('appPollsManager sendVote updates:', updates);
apiUpdatesManager.processUpdateMessage(updates);
});
}
} }
export default new AppPollsManager(); export default new AppPollsManager();

2
src/lib/smoothscroll.js Normal file
View File

@ -0,0 +1,2 @@
// credits to https://github.com/iamdustan/smoothscroll
!function(){"use strict";function o(){var o=window,t=document;if(!("scrollBehavior"in t.documentElement.style&&!0!==o.__forceSmoothScrollPolyfill__)){var l,e=o.HTMLElement||o.Element,r=468,i={scroll:o.scroll||o.scrollTo,scrollBy:o.scrollBy,elementScroll:e.prototype.scroll||n,scrollIntoView:e.prototype.scrollIntoView},s=o.performance&&o.performance.now?o.performance.now.bind(o.performance):Date.now,c=(l=o.navigator.userAgent,new RegExp(["MSIE ","Trident/","Edge/"].join("|")).test(l)?1:0);o.scroll=o.scrollTo=function(){void 0!==arguments[0]&&(!0!==f(arguments[0])?h.call(o,t.body,void 0!==arguments[0].left?~~arguments[0].left:o.scrollX||o.pageXOffset,void 0!==arguments[0].top?~~arguments[0].top:o.scrollY||o.pageYOffset):i.scroll.call(o,void 0!==arguments[0].left?arguments[0].left:"object"!=typeof arguments[0]?arguments[0]:o.scrollX||o.pageXOffset,void 0!==arguments[0].top?arguments[0].top:void 0!==arguments[1]?arguments[1]:o.scrollY||o.pageYOffset))},o.scrollBy=function(){void 0!==arguments[0]&&(f(arguments[0])?i.scrollBy.call(o,void 0!==arguments[0].left?arguments[0].left:"object"!=typeof arguments[0]?arguments[0]:0,void 0!==arguments[0].top?arguments[0].top:void 0!==arguments[1]?arguments[1]:0):h.call(o,t.body,~~arguments[0].left+(o.scrollX||o.pageXOffset),~~arguments[0].top+(o.scrollY||o.pageYOffset)))},e.prototype.scroll=e.prototype.scrollTo=function(){if(void 0!==arguments[0])if(!0!==f(arguments[0])){var o=arguments[0].left,t=arguments[0].top;h.call(this,this,void 0===o?this.scrollLeft:~~o,void 0===t?this.scrollTop:~~t)}else{if("number"==typeof arguments[0]&&void 0===arguments[1])throw new SyntaxError("Value could not be converted");i.elementScroll.call(this,void 0!==arguments[0].left?~~arguments[0].left:"object"!=typeof arguments[0]?~~arguments[0]:this.scrollLeft,void 0!==arguments[0].top?~~arguments[0].top:void 0!==arguments[1]?~~arguments[1]:this.scrollTop)}},e.prototype.scrollBy=function(){void 0!==arguments[0]&&(!0!==f(arguments[0])?this.scroll({left:~~arguments[0].left+this.scrollLeft,top:~~arguments[0].top+this.scrollTop,behavior:arguments[0].behavior}):i.elementScroll.call(this,void 0!==arguments[0].left?~~arguments[0].left+this.scrollLeft:~~arguments[0]+this.scrollLeft,void 0!==arguments[0].top?~~arguments[0].top+this.scrollTop:~~arguments[1]+this.scrollTop))},e.prototype.scrollIntoView=function(){if(!0!==f(arguments[0])){var l=function(o){for(;o!==t.body&&!1===(e=p(l=o,"Y")&&a(l,"Y"),r=p(l,"X")&&a(l,"X"),e||r);)o=o.parentNode||o.host;var l,e,r;return o}(this),e=l.getBoundingClientRect(),r=this.getBoundingClientRect();l!==t.body?(h.call(this,l,l.scrollLeft+r.left-e.left,l.scrollTop+r.top-e.top),"fixed"!==o.getComputedStyle(l).position&&o.scrollBy({left:e.left,top:e.top,behavior:"smooth"})):o.scrollBy({left:r.left,top:r.top,behavior:"smooth"})}else i.scrollIntoView.call(this,void 0===arguments[0]||arguments[0])}}function n(o,t){this.scrollLeft=o,this.scrollTop=t}function f(o){if(null===o||"object"!=typeof o||void 0===o.behavior||"auto"===o.behavior||"instant"===o.behavior)return!0;if("object"==typeof o&&"smooth"===o.behavior)return!1;throw new TypeError("behavior member of ScrollOptions "+o.behavior+" is not a valid value for enumeration ScrollBehavior.")}function p(o,t){return"Y"===t?o.clientHeight+c<o.scrollHeight:"X"===t?o.clientWidth+c<o.scrollWidth:void 0}function a(t,l){var e=o.getComputedStyle(t,null)["overflow"+l];return"auto"===e||"scroll"===e}function d(t){var l,e,i,c,n=(s()-t.startTime)/r;c=n=n>1?1:n,l=.5*(1-Math.cos(Math.PI*c)),e=t.startX+(t.x-t.startX)*l,i=t.startY+(t.y-t.startY)*l,t.method.call(t.scrollable,e,i),e===t.x&&i===t.y||o.requestAnimationFrame(d.bind(o,t))}function h(l,e,r){var c,f,p,a,h=s();l===t.body?(c=o,f=o.scrollX||o.pageXOffset,p=o.scrollY||o.pageYOffset,a=i.scroll):(c=l,f=l.scrollLeft,p=l.scrollTop,a=n),d({scrollable:c,method:a,startTime:h,startX:f,startY:p,x:e,y:r})}}"object"==typeof exports&&"undefined"!=typeof module?module.exports={polyfill:o}:o()}();

View File

@ -288,7 +288,7 @@ export function getSelectedText() {
export const $rootScope = { export const $rootScope = {
$broadcast: (name/* : string */, detail/*? : any */) => { $broadcast: (name/* : string */, detail/*? : any */) => {
//console.log(dT(), 'Broadcasting ' + name + ' event, with args:', detail); console.log(dT(), 'Broadcasting ' + name + ' event, with args:', detail);
//console.trace(); //console.trace();
let myCustomEvent = new CustomEvent(name, {detail}); let myCustomEvent = new CustomEvent(name, {detail});
document.dispatchEvent(myCustomEvent); document.dispatchEvent(myCustomEvent);

View File

@ -5,6 +5,7 @@ import Config from '../lib/config';
import { findUpTag } from "../lib/utils"; import { findUpTag } from "../lib/utils";
import pageAuthCode from "./pageAuthCode"; import pageAuthCode from "./pageAuthCode";
import pageSignQR from './pageSignQR';
//import apiManager from "../lib/mtproto/apiManager"; //import apiManager from "../lib/mtproto/apiManager";
import apiManager from "../lib/mtproto/mtprotoworker"; import apiManager from "../lib/mtproto/mtprotoworker";
import Page from "./page"; import Page from "./page";
@ -54,6 +55,10 @@ let onFirstMount = () => {
let initedSelect = false; let initedSelect = false;
page.pageEl.querySelector('.a-qr').addEventListener('click', () => {
pageSignQR.mount();
});
selectCountryCode.addEventListener('focus', function(this: typeof selectCountryCode, e) { selectCountryCode.addEventListener('focus', function(this: typeof selectCountryCode, e) {
/* this.removeAttribute('readonly'); */ /* this.removeAttribute('readonly'); */
if(!initedSelect) { if(!initedSelect) {
@ -114,7 +119,7 @@ let onFirstMount = () => {
parent.appendChild(wrapper); parent.appendChild(wrapper);
}/* , {once: true} */); }/* , {once: true} */);
selectCountryCode.addEventListener('blur', function(this: typeof selectCountryCode, e) { selectCountryCode.addEventListener('blur', function(this: typeof selectCountryCode, e) {
//parent.removeChild(wrapper); parent.removeChild(wrapper);
e.cancelBubble = true; e.cancelBubble = true;
}, {capture: true}); }, {capture: true});

View File

@ -3,6 +3,7 @@ import apiManager from '../lib/mtproto/mtprotoworker';
import Page from './page'; import Page from './page';
import pageIm from './pageIm'; import pageIm from './pageIm';
import pagePassword from './pagePassword'; import pagePassword from './pagePassword';
import pageSignIn from './pageSignIn';
import { App } from '../lib/mtproto/mtproto_config'; import { App } from '../lib/mtproto/mtproto_config';
import { bytesToBase64, bytesCmp } from '../lib/bin_utils'; import { bytesToBase64, bytesCmp } from '../lib/bin_utils';
import serverTimeManager from '../lib/mtproto/serverTimeManager'; import serverTimeManager from '../lib/mtproto/serverTimeManager';
@ -53,6 +54,10 @@ let onFirstMount = async() => {
const pageElement = page.pageEl; const pageElement = page.pageEl;
const imageDiv = pageElement.querySelector('.auth-image') as HTMLDivElement; const imageDiv = pageElement.querySelector('.auth-image') as HTMLDivElement;
page.pageEl.querySelector('.a-qr').addEventListener('click', () => {
pageSignIn.mount();
});
const results = await Promise.all([ const results = await Promise.all([
import('qr-code-styling' as any) import('qr-code-styling' as any)
]); ]);

View File

@ -362,6 +362,11 @@ $time-background: rgba(0, 0, 0, 0.35);
background-position: center center; background-position: center center;
} }
i {
font-style: normal;
color: $color-blue;
}
img.emoji { img.emoji {
height: 16px; height: 16px;
width: 16px; width: 16px;

View File

@ -6,6 +6,18 @@
max-width: $chat-max-width; max-width: $chat-max-width;
margin: 0 auto; margin: 0 auto;
&.is-selected {
&:before {
position: absolute;
width: 100%;
height: 100%;
background-color: #7ca09f;
content: " ";
display: block;
left: 0;
}
}
&.is-date { &.is-date {
position: -webkit-sticky; position: -webkit-sticky;
position: sticky; position: sticky;
@ -106,7 +118,7 @@
} }
} }
&:not(.is-group-last) .user-avatar { &:not(.is-group-last) .bubble__container > .user-avatar {
display: none; display: none;
} }
@ -188,7 +200,7 @@
} }
} }
&.emoji-1x { &.emoji-1x .attachment {
font-size: 96px; font-size: 96px;
img.emoji { img.emoji {
@ -199,7 +211,7 @@
} }
} }
&.emoji-2x { &.emoji-2x .attachment {
font-size: 64px; font-size: 64px;
img.emoji { img.emoji {
@ -210,7 +222,7 @@
} }
} }
&.emoji-3x { &.emoji-3x .attachment {
font-size: 52px; font-size: 52px;
img.emoji { img.emoji {
@ -590,6 +602,10 @@
content: "\e929"; content: "\e929";
margin-right: -2px; margin-right: -2px;
} }
.time {
width: unset;
}
} }
.message.contact-message { .message.contact-message {
@ -599,6 +615,7 @@
.contact { .contact {
display: flex; display: flex;
padding: 2px 0; padding: 2px 0;
cursor: pointer;
&-avatar { &-avatar {
color: #fff; color: #fff;
@ -639,6 +656,8 @@
&-name { &-name {
line-height: 1.4; line-height: 1.4;
margin-top: 1px; margin-top: 1px;
overflow: hidden;
text-overflow: ellipsis;
} }
} }
} }
@ -844,15 +863,15 @@
cursor: pointer; cursor: pointer;
margin-right: 5px; margin-right: 5px;
} }
img.emoji {
margin-bottom: 3px;
}
} }
} }
.bubble-audio.is-in .time { .bubble-audio .time {
width: inherit; width: unset !important;
}
.bubble-audio.is-out .time {
width: inherit;
} }
.bubble.is-in { .bubble.is-in {
@ -959,6 +978,14 @@
} }
} }
} }
/* .poll {
&-answer-selected {
&:before {
margin-left: -1px;
}
}
} */
} }
.bubble.is-out { .bubble.is-out {
@ -1122,6 +1149,10 @@
stroke: #4fae4e; stroke: #4fae4e;
} }
&-answer-selected {
background-color: #4fae4e;
}
&-answer:hover { &-answer:hover {
.animation-ring { .animation-ring {
background-color: rgba(79, 174, 78, 0.08); background-color: rgba(79, 174, 78, 0.08);
@ -1163,7 +1194,7 @@ poll-element {
&-text { &-text {
margin-top: 7px; margin-top: 7px;
margin-left: 7px; margin-left: 14px;
} }
&-percents { &-percents {
@ -1174,7 +1205,26 @@ poll-element {
font-weight: 500; font-weight: 500;
margin-top: 7px; margin-top: 7px;
transition: .34s opacity; transition: .34s opacity;
margin-left: -1px; margin-left: -3px;
text-align: right;
width: 40px;
}
&-selected {
position: absolute;
top: 33px;
left: 26px;
color: #fff;
background: #50a2e9;
border-radius: 50%;
height: 12px;
width: 12px;
font-size: 11px;
line-height: 15px;
opacity: 0;
animation: fadeIn .1s ease forwards;
animation-direction: reverse;
animation-delay: .24s;
} }
&:hover { &:hover {
@ -1182,7 +1232,9 @@ poll-element {
visibility: visible; visibility: visible;
transform: scale(1); transform: scale(1);
} }
}
&.is-voting {
.progress-ring__circle { .progress-ring__circle {
stroke-dashoffset: -19.792; stroke-dashoffset: -19.792;
animation: pollAnswerRotate 0.65s linear infinite; animation: pollAnswerRotate 0.65s linear infinite;
@ -1197,13 +1249,16 @@ poll-element {
} }
&-line { &-line {
height: 28px; height: 35px;
position: absolute; position: absolute;
left: 11.5px; left: 17.5px;
top: 14px; top: 11px;
transition: stroke-dashoffset .34s linear, stroke-dasharray .34s linear;
stroke-dashoffset: 0;
stroke-dasharray: 0, 485.9;
use { use {
stroke-width: 3.5px; stroke-width: 4px;
stroke-linecap: round; stroke-linecap: round;
stroke: #50a2e9; stroke: #50a2e9;
fill: none; fill: none;
@ -1223,7 +1278,7 @@ poll-element {
align-items: center; align-items: center;
width: 34px; width: 34px;
height: 34px; height: 34px;
margin-left: -1px; margin-left: 5px;
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
@ -1256,6 +1311,7 @@ poll-element {
stroke-dashoffset: 0; stroke-dashoffset: 0;
stroke-opacity: 1; stroke-opacity: 1;
stroke-width: 2; stroke-width: 2;
stroke: #8d969c;
fill: transparent; fill: transparent;
} }
} }
@ -1268,6 +1324,20 @@ poll-element {
.poll-answer-percents { .poll-answer-percents {
opacity: 1; opacity: 1;
} }
.poll-answer-selected {
animation-direction: normal;
}
}
&.is-retracting {
.circle-hover {
transition-delay: .24s;
}
.animation-ring {
transition-delay: .22s;
}
} }
} }

View File

@ -102,10 +102,6 @@
} }
} }
/* li.dialog-pinned + .pinned-delimiter {
display: flex;
} */
p { p {
margin: 0; margin: 0;
display: flex; display: flex;

View File

@ -634,6 +634,20 @@ input {
} }
} }
.page-sign, .page-signQR {
.qr {
margin-top: 1.5rem;
}
p.qr-description {
color: #707579;
line-height: 1.85;
text-align: left;
margin-left: auto;
margin-right: auto;
}
}
/* .page-signQR { /* .page-signQR {
.auth-image { .auth-image {
position: relative; position: relative;