Poll vote & update & scroll chat fix & QR page & chat list updates
This commit is contained in:
parent
a884a9cea8
commit
a6d9d69909
@ -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 { findUpClassName, $rootScope } from "../lib/utils";
|
||||
|
||||
let lineTotalLength = 0;
|
||||
const tailLength = 9;
|
||||
@ -7,113 +8,8 @@ const times = 10;
|
||||
const fullTime = 340;
|
||||
const oneTime = fullTime / times;
|
||||
|
||||
export default class PollElement extends HTMLElement {
|
||||
private svgLines: SVGSVGElement[];
|
||||
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 roundPercents = (percents: number[]) => {
|
||||
//console.log('roundPercents before percents:', percents);
|
||||
|
||||
let sum = percents.reduce((acc, p) => acc + Math.round(p), 0);
|
||||
if(sum > 100) {
|
||||
@ -135,7 +31,7 @@ export default class PollElement extends HTMLElement {
|
||||
|
||||
percents[minIndex] -= minRemainder;
|
||||
}
|
||||
} else {
|
||||
} else if(sum < 100) {
|
||||
let diff = 100 - sum;
|
||||
let length = percents.length;
|
||||
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();
|
||||
let r = () => {
|
||||
let diff = Date.now() - start;
|
||||
let progress = diff / fullTime;
|
||||
if(progress > 1) progress = 1;
|
||||
const connectedPolls: {id: string, element: PollElement}[] = [];
|
||||
$rootScope.$on('poll_update', (e: CustomEvent) => {
|
||||
let {poll, results} = e.detail as {poll: Poll, results: PollResults};
|
||||
|
||||
this.svgLines.forEach((svg, idx) => {
|
||||
this.setLineProgress(idx, progress);
|
||||
for(let connected of connectedPolls) {
|
||||
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) {
|
||||
setTimeout(() => {
|
||||
percents.forEach((percents, idx) => {
|
||||
let value = Math.round(percents / times * (i + 1));
|
||||
let div = this.numberDivs[idx];
|
||||
//div.style.opacity = ((i + 1) * 0.10).toFixed(1); // опасити в 10 шагов от 0.1 до 1
|
||||
div.innerText = value + '%';
|
||||
this.numberDivs[idx].innerText = value + '%';
|
||||
});
|
||||
}, 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');
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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.strokeDashoffset = '' + percents * this.maxOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// у элемента могут быть ещё другие методы и свойства
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { logger, deferredPromise, CancellablePromise } from "../lib/polyfill";
|
||||
|
||||
import smoothscroll from '../lib/smoothscroll';
|
||||
(window as any).__forceSmoothScrollPolyfill__ = true;
|
||||
smoothscroll.polyfill();
|
||||
/*
|
||||
var el = $0;
|
||||
var height = 0;
|
||||
@ -66,6 +68,8 @@ export default class Scrollable {
|
||||
private lastBottomID = 0;
|
||||
private lastScrollDirection = 0; // true = bottom
|
||||
|
||||
private scrollLocked = 0;
|
||||
|
||||
private setVisible(element: HTMLElement) {
|
||||
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) {
|
||||
this.appendTo = this.container;
|
||||
}
|
||||
@ -326,7 +275,7 @@ export default class Scrollable {
|
||||
let scrollTop = scrollPos - this.scrollTopOffset;
|
||||
let maxScrollTop = this.scrollSize - this.scrollTopOffset - this.size;
|
||||
|
||||
if(this.onScrolledBottom) {
|
||||
if(this.onScrolledBottom && !this.scrollLocked) {
|
||||
if((maxScrollTop - scrollTop) <= this.onScrollOffset) {
|
||||
//if(!this.onScrolledBottomFired) {
|
||||
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);
|
||||
if(scrollTop <= this.onScrollOffset) {
|
||||
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) {
|
||||
element.dataset.virtual = '' + (append ? this.virtualTempIDBottom++ : this.virtualTempIDTop--);
|
||||
|
||||
@ -401,9 +367,29 @@ export default class Scrollable {
|
||||
return !!element.parentElement;
|
||||
}
|
||||
|
||||
public scrollIntoView(element: Element) {
|
||||
public scrollIntoView(element: HTMLElement) {
|
||||
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'});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -875,22 +875,7 @@ export function wrapReply(title: string, subtitle: string, message?: any) {
|
||||
|
||||
let media = message && message.media;
|
||||
if(media) {
|
||||
if(message.grouped_id) {
|
||||
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._;
|
||||
}
|
||||
replySubtitle.innerHTML = message.rReply;
|
||||
|
||||
if(media.photo || (media.document && ['video'].indexOf(media.document.type) !== -1)) {
|
||||
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();
|
||||
elem.setAttribute('poll-id', poll.id);
|
||||
elem.setAttribute('poll-id', pollID);
|
||||
elem.setAttribute('message-id', '' + mid);
|
||||
return elem;
|
||||
}
|
||||
|
@ -143,28 +143,41 @@ export class AppDialogsManager {
|
||||
let dialog: any = e.detail;
|
||||
|
||||
this.setLastMessage(dialog);
|
||||
this.setUnreadMessages(dialog);
|
||||
this.setDialogPosition(dialog);
|
||||
|
||||
this.setPinnedDelimiter();
|
||||
});
|
||||
|
||||
$rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => {
|
||||
let dialogs = e.detail;
|
||||
|
||||
let performed = 0;
|
||||
for(let id in dialogs) {
|
||||
let dialog = dialogs[id];
|
||||
|
||||
/////console.log('updating dialog:', dialog);
|
||||
|
||||
++performed;
|
||||
|
||||
if(!(dialog.peerID in this.doms)) {
|
||||
this.addDialog(dialog);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.setLastMessage(dialog);
|
||||
this.setUnreadMessages(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) => {
|
||||
@ -184,6 +197,7 @@ export class AppDialogsManager {
|
||||
});
|
||||
|
||||
this.loadDialogs().then(result => {
|
||||
this.setPinnedDelimiter();
|
||||
//appSidebarLeft.onChatsScroll();
|
||||
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 dom = this.getDialogDom(dialog.peerID);
|
||||
let prevPos = whichChild(dom.listEl);
|
||||
|
||||
if(prevPos == pos) {
|
||||
return;
|
||||
} else if(prevPos < pos) { // was higher
|
||||
@ -348,103 +363,45 @@ export class AppDialogsManager {
|
||||
chatList.append(dom.listEl);
|
||||
}
|
||||
|
||||
// fix order
|
||||
(Array.from(chatList.children) as HTMLElement[]).forEach((el, idx) => {
|
||||
el.dataset.virtual = '' + idx;
|
||||
});
|
||||
(dialog.folder_id == 1 ? this.scrollArchived : this.scroll).reorder();
|
||||
|
||||
this.log('setDialogPosition:', dialog, dom, pos);
|
||||
}
|
||||
|
||||
/* public sortDom(archived = false) {
|
||||
//if(archived) return;
|
||||
|
||||
let dialogs = appMessagesManager.dialogsStorage.dialogs.slice();
|
||||
|
||||
let inUpper: Scrollable['hiddenElements']['up'] = [];
|
||||
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);
|
||||
public setPinnedDelimiter() {
|
||||
let index = -1;
|
||||
let dialogs = appMessagesManager.dialogsStorage.dialogs[0];
|
||||
for(let dialog of dialogs) {
|
||||
if(dialog.pFlags?.pinned) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if(pinnedDialogs.length) {
|
||||
let dom = this.getDialogDom(pinnedDialogs[pinnedDialogs.length - 1].peerID);
|
||||
if(dom) {
|
||||
dom.listEl.append(this.pinnedDelimiter);
|
||||
let currentIndex = (this.pinnedDelimiter.parentElement && whichChild(this.pinnedDelimiter.parentElement)) ?? -1;
|
||||
|
||||
if(index == currentIndex) return;
|
||||
|
||||
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 {
|
||||
if(this.pinnedDelimiter.parentElement) {
|
||||
this.pinnedDelimiter.parentElement.removeChild(this.pinnedDelimiter);
|
||||
}
|
||||
this.pinnedDelimiter.remove();
|
||||
}
|
||||
|
||||
sorted = sorted.filter((d: any) => !d.pFlags.pinned && d.folder_id != 1);
|
||||
} else {
|
||||
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;
|
||||
modifying.forEach(elem => {
|
||||
this.scroll.updateElement(elem);
|
||||
});
|
||||
|
||||
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) {
|
||||
if(!lastMessage) {
|
||||
lastMessage = appMessagesManager.getMessage(dialog.top_message);
|
||||
@ -465,94 +422,7 @@ export class AppDialogsManager {
|
||||
//console.log('setting last message:', lastMessage);
|
||||
|
||||
/* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ {
|
||||
let lastMessageText = '';
|
||||
|
||||
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 messageText = lastMessage.message;
|
||||
let messageWrapped = '';
|
||||
if(messageText) {
|
||||
let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '), {noLinebreakers: true});
|
||||
@ -577,9 +447,10 @@ export class AppDialogsManager {
|
||||
entities: entities,
|
||||
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(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);
|
||||
|
||||
dom.statusSpan.innerHTML = '';
|
||||
let lastMessage = appMessagesManager.getMessage(dialog.top_message);
|
||||
if(lastMessage._ != 'messageEmpty' &&
|
||||
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');
|
||||
|
||||
dom.unreadMessagesSpan.innerHTML = '';
|
||||
if(dialog.unread_count) {
|
||||
dom.unreadMessagesSpan.innerHTML = dialog.unread_count;
|
||||
dom.unreadMessagesSpan.innerText = '';
|
||||
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() ?
|
||||
'unread-muted' : 'unread');
|
||||
} else if(dialog.pFlags.pinned) {
|
||||
@ -826,12 +697,6 @@ export class AppDialogsManager {
|
||||
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);
|
||||
} else {
|
||||
container.append(li);
|
||||
|
@ -274,7 +274,7 @@ export class AppImManager {
|
||||
/////this.log('setting pinned message', message);
|
||||
this.pinnedMessageContainer.dataset.mid = '' + mid;
|
||||
this.pinnedMessageContainer.style.display = '';
|
||||
this.pinnedMessageContent.innerHTML = RichTextProcessor.wrapEmojiText(message.message);
|
||||
this.pinnedMessageContent.innerHTML = message.rReply;
|
||||
}
|
||||
|
||||
this.needUpdate.forEachReverse((obj, idx) => {
|
||||
@ -329,11 +329,17 @@ export class AppImManager {
|
||||
let target = e.target as HTMLElement;
|
||||
let bubble: HTMLDivElement = null;
|
||||
try {
|
||||
bubble = findUpClassName(e.target, 'bubble');
|
||||
bubble = findUpClassName(target, 'bubble');
|
||||
} catch(err) {}
|
||||
|
||||
if(!bubble) return;
|
||||
|
||||
let contactDiv = findUpClassName(target, 'contact');
|
||||
if(contactDiv) {
|
||||
this.setPeer(+contactDiv.dataset.peerID);
|
||||
return;
|
||||
}
|
||||
|
||||
//this.log('chatInner click:', target);
|
||||
if(target.tagName == 'SPAN') {
|
||||
(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);
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
if(onlines > 1) {
|
||||
@ -1091,9 +1097,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);
|
||||
@ -1716,6 +1722,8 @@ export class AppImManager {
|
||||
|
||||
let contactDiv = document.createElement('div');
|
||||
contactDiv.classList.add('contact');
|
||||
contactDiv.dataset.peerID = '' + message.media.user_id;
|
||||
|
||||
messageDiv.classList.add('contact-message');
|
||||
processingWebPage = true;
|
||||
|
||||
@ -1724,12 +1732,16 @@ export class AppImManager {
|
||||
if(message.media.last_name) texts.push(RichTextProcessor.wrapEmojiText(message.media.last_name));
|
||||
|
||||
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-name">${texts.join(' ')}</div>
|
||||
<div class="contact-number">${message.media.phone_number ? '+' + formatPhoneNumber(message.media.phone_number).formatted : 'Unknown phone number'}</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');
|
||||
messageDiv.append(contactDiv);
|
||||
|
||||
@ -1739,7 +1751,7 @@ export class AppImManager {
|
||||
case 'messageMediaPoll': {
|
||||
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);
|
||||
|
||||
break;
|
||||
@ -1927,11 +1939,14 @@ export class AppImManager {
|
||||
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');
|
||||
|
||||
//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);
|
||||
|
||||
@ -1981,21 +1996,27 @@ export class AppImManager {
|
||||
} */
|
||||
|
||||
if(!renderedFirstScreen) {
|
||||
if(!this.scrollable.scrollTop) {
|
||||
/* if(!this.scrollable.scrollTop) {
|
||||
let height = bubble.scrollHeight;
|
||||
//let height = Math.ceil(bubble.getBoundingClientRect().height);
|
||||
this.scrollable.scrollTop += height;
|
||||
//innerHeight -= height;
|
||||
}
|
||||
/* if(leftHeightToScroll >= 0) {
|
||||
} */
|
||||
/* if(leftHeightToScroll > 0) {
|
||||
let height = bubble.scrollHeight;
|
||||
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;
|
||||
resolve();
|
||||
resolved = true;
|
||||
}
|
||||
//}
|
||||
}
|
||||
} else {
|
||||
////////this.scrollable.append(bubble);
|
||||
@ -2026,6 +2047,14 @@ export class AppImManager {
|
||||
};
|
||||
|
||||
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();
|
||||
/* method((msgID) => {
|
||||
let message = appMessagesManager.getMessage(msgID);
|
||||
@ -2054,7 +2083,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 = 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;
|
||||
|
||||
if(testScroll) {
|
||||
@ -2122,10 +2151,9 @@ export class AppImManager {
|
||||
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 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(reverse) {
|
||||
//ids = ids.slice(-removeCount);
|
||||
|
@ -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 appChatsManager from "./appChatsManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
@ -106,15 +106,13 @@ export class AppMessagesManager {
|
||||
public pinnedIndex = 0;
|
||||
public dialogsNum = 0;
|
||||
|
||||
public migratedFromTo: any = {};
|
||||
public migratedToFrom: any = {};
|
||||
public migratedFromTo: {[peerID: number]: number} = {};
|
||||
public migratedToFrom: {[peerID: number]: number} = {};
|
||||
|
||||
public newMessagesHandlePromise = 0;
|
||||
public newMessagesToHandle: any = {};
|
||||
public newDialogsHandlePromise = 0;
|
||||
public newDialogsToHandle: any = {};
|
||||
//public notificationsHandlePromise = 0;
|
||||
//public notificationsToHandle: any = {};
|
||||
public newDialogsToHandle: {[peerID: string]: {reload: true} | Dialog} = {};
|
||||
public newUpdatesAfterReloadToHandle: any = {};
|
||||
|
||||
public fwdMessagesPluralize = _('conversation_forwarded_X_messages');
|
||||
@ -125,7 +123,7 @@ export class AppMessagesManager {
|
||||
if(maxID && !appMessagesIDsManager.getMessageIDInfo(maxID)[1]) {
|
||||
this.maxSeenID = maxID;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
let update: any = e.detail;
|
||||
@ -183,7 +181,7 @@ export class AppMessagesManager {
|
||||
index: dialog.index
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
public getInputEntities(entities: any) {
|
||||
@ -248,7 +246,7 @@ export class AppMessagesManager {
|
||||
entities: this.getInputEntities(entities),
|
||||
no_webpage: noWebPage,
|
||||
}).then((updates) => {
|
||||
apiUpdatesManager.processUpdateMessage(updates)
|
||||
apiUpdatesManager.processUpdateMessage(updates);
|
||||
}, (error) => {
|
||||
if(error && error.type == 'MESSAGE_NOT_MODIFIED') {
|
||||
error.handled = true;
|
||||
@ -1129,20 +1127,6 @@ export class AppMessagesManager {
|
||||
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) {
|
||||
let curDialogStorage = this.dialogsStorage.dialogs[folderID] ?? (this.dialogsStorage.dialogs[folderID] = []);
|
||||
|
||||
@ -1327,13 +1311,47 @@ export class AppMessagesManager {
|
||||
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) {
|
||||
if(date === undefined) {
|
||||
date = tsNow(true) + serverTimeManager.serverTimeOffset;
|
||||
}
|
||||
|
||||
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) {
|
||||
let dialogs = this.dialogsStorage.dialogs[dialog.folder_id] ?? (this.dialogsStorage.dialogs[dialog.folder_id] = []);
|
||||
let pos = this.getDialogByPeerID(dialog.peerID)[1];
|
||||
@ -1504,7 +1522,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
break;
|
||||
case 'messageMediaPoll':
|
||||
appPollsManager.savePoll(apiMessage.media.poll, apiMessage.media.results);
|
||||
apiMessage.media.poll = appPollsManager.savePoll(apiMessage.media.poll, apiMessage.media.results);
|
||||
break;
|
||||
case 'messageMediaDocument':
|
||||
if(apiMessage.media.ttl_seconds) {
|
||||
@ -1512,6 +1530,8 @@ export class AppMessagesManager {
|
||||
} else {
|
||||
apiMessage.media.document = appDocsManager.saveDoc(apiMessage.media.document, mediaContext); // 11.04.2020 warning
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case 'messageMediaWebPage':
|
||||
/* if(apiMessage.media.webpage.document) {
|
||||
@ -1615,6 +1635,8 @@ export class AppMessagesManager {
|
||||
}
|
||||
}
|
||||
|
||||
apiMessage.rReply = this.getRichReplyText(apiMessage);
|
||||
|
||||
if(apiMessage.message && apiMessage.message.length) {
|
||||
var myEntities = RichTextProcessor.parseEntities(apiMessage.message);
|
||||
var apiEntities = apiMessage.entities || [];
|
||||
@ -1626,7 +1648,110 @@ export class AppMessagesManager {
|
||||
if(!options.isEdited) {
|
||||
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) {
|
||||
@ -1644,7 +1769,7 @@ export class AppMessagesManager {
|
||||
var foundDialog = this.getDialogByPeerID(migrateFrom);
|
||||
if(foundDialog.length) {
|
||||
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});
|
||||
@ -1703,7 +1828,7 @@ export class AppMessagesManager {
|
||||
|
||||
//console.log('applyConversation', dialogsResult);
|
||||
|
||||
var updatedDialogs: any = {};
|
||||
var updatedDialogs: {[peerID: number]: Dialog} = {};
|
||||
var hasUpdated = false;
|
||||
dialogsResult.dialogs.forEach((dialog: any) => {
|
||||
var peerID = AppPeersManager.getPeerID(dialog.peer);
|
||||
@ -1738,7 +1863,7 @@ export class AppMessagesManager {
|
||||
var foundDialog = this.getDialogByPeerID(peerID);
|
||||
if(foundDialog.length) {
|
||||
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_outbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_outbox_max_id, channelID);
|
||||
|
||||
var topDate = message.date;
|
||||
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);
|
||||
this.generateIndexForDialog(dialog);
|
||||
dialog.peerID = peerID;
|
||||
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;
|
||||
|
||||
if(dialog) {
|
||||
@ -2174,10 +2282,10 @@ export class AppMessagesManager {
|
||||
clearTimeout(this.newDialogsHandlePromise);
|
||||
this.newDialogsHandlePromise = 0;
|
||||
|
||||
var newMaxSeenID = 0
|
||||
let newMaxSeenID = 0;
|
||||
Object.keys(this.newDialogsToHandle).forEach((peerID) => {
|
||||
let dialog = this.newDialogsToHandle[peerID];
|
||||
if(dialog.reload) {
|
||||
if('reload' in dialog) {
|
||||
this.reloadConversation(+peerID);
|
||||
delete this.newDialogsToHandle[peerID];
|
||||
} else {
|
||||
@ -2186,7 +2294,9 @@ export class AppMessagesManager {
|
||||
newMaxSeenID = Math.max(newMaxSeenID, dialog.top_message || 0);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
console.log('after order:', this.dialogsStorage.dialogs[0].map(d => d.peerID));
|
||||
|
||||
if(newMaxSeenID != 0) {
|
||||
this.incrementMaxSeenID(newMaxSeenID);
|
||||
@ -2339,7 +2449,7 @@ 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;
|
||||
@ -2469,26 +2579,37 @@ export class AppMessagesManager {
|
||||
break;
|
||||
}
|
||||
|
||||
/* case 'updateDialogPinned': {
|
||||
var peerID = AppPeersManager.getPeerID(update.peer);
|
||||
var foundDialog = this.getDialogByPeerID(peerID);
|
||||
case 'updateDialogPinned': {
|
||||
console.log('updateDialogPinned', update);
|
||||
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) {
|
||||
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
|
||||
}
|
||||
|
||||
if(!foundDialog.length) {
|
||||
this.newDialogsToHandle[peerID] = {reload: true};
|
||||
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;
|
||||
}
|
||||
|
||||
case 'updatePinnedDialogs': {
|
||||
var newPinned: any = {};
|
||||
console.log('updatePinnedDialogs', update);
|
||||
let newPinned: {[peerID: number]: true} = {};
|
||||
if(!update.order) {
|
||||
apiManager.invokeApi('messages.getPinnedDialogs', {}).then((dialogsResult: any) => {
|
||||
dialogsResult.dialogs.reverse();
|
||||
@ -2498,8 +2619,8 @@ export class AppMessagesManager {
|
||||
newPinned[dialog.peerID] = true;
|
||||
});
|
||||
|
||||
this.dialogsStorage.dialogs.forEach((dialog: any) => {
|
||||
var peerID = dialog.peerID;
|
||||
this.dialogsStorage.dialogs[0].forEach((dialog: any) => {
|
||||
let peerID = dialog.peerID;
|
||||
if(dialog.pFlags.pinned && !newPinned[peerID]) {
|
||||
this.newDialogsToHandle[peerID] = {reload: true};
|
||||
if(!this.newDialogsHandlePromise) {
|
||||
@ -2511,42 +2632,45 @@ export class AppMessagesManager {
|
||||
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) => {
|
||||
var peerID = AppPeersManager.getPeerID(peer);
|
||||
let peerID = AppPeersManager.getPeerID(peer.peer);
|
||||
newPinned[peerID] = true;
|
||||
|
||||
var foundDialog = this.getDialogByPeerID(peerID);
|
||||
|
||||
let foundDialog = this.getDialogByPeerID(peerID);
|
||||
if(!foundDialog.length) {
|
||||
this.newDialogsToHandle[peerID] = {reload: true}
|
||||
if(!this.newDialogsHandlePromise) {
|
||||
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
|
||||
}
|
||||
this.newDialogsToHandle[peerID] = {reload: true};
|
||||
willHandle = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var dialog = foundDialog[0]
|
||||
dialog.index = this.generateDialogIndex(this.generateDialogPinnedDate(dialog));
|
||||
let dialog = foundDialog[0];
|
||||
delete dialog.pinnedIndex;
|
||||
dialog.pFlags.pinned = true;
|
||||
this.generateIndexForDialog(dialog);
|
||||
|
||||
this.newDialogsToHandle[peerID] = dialog
|
||||
if(!this.newDialogsHandlePromise) {
|
||||
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
|
||||
}
|
||||
})
|
||||
this.newDialogsToHandle[peerID] = dialog;
|
||||
willHandle = true;
|
||||
});
|
||||
|
||||
this.dialogsStorage.dialogs.forEach((dialog: any) => {
|
||||
var peerID = dialog.peerID;
|
||||
this.dialogsStorage.dialogs[0].forEach(dialog => {
|
||||
let peerID = dialog.peerID;
|
||||
if(dialog.pFlags.pinned && !newPinned[peerID]) {
|
||||
this.newDialogsToHandle[peerID] = {reload: true}
|
||||
if(!this.newDialogsHandlePromise) {
|
||||
this.newDialogsToHandle[peerID] = {reload: true};
|
||||
willHandle = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(willHandle && !this.newDialogsHandlePromise) {
|
||||
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
break;
|
||||
} */
|
||||
}
|
||||
|
||||
case 'updateEditMessage':
|
||||
case 'updateEditChannelMessage': {
|
||||
@ -2819,7 +2943,7 @@ export class AppMessagesManager {
|
||||
} else {
|
||||
if(foundDialog[0]) {
|
||||
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]) {
|
||||
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 offsetNotFound = false;
|
||||
var unreadOffset = 0;
|
||||
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 reqPeerID = 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) {
|
||||
offsetNotFound = true;
|
||||
for(offset = 0; offset < historyStorage.history.length; offset++) {
|
||||
@ -3035,7 +3126,7 @@ export class AppMessagesManager {
|
||||
offset = Math.max(0, offset - backLimit);
|
||||
limit += backLimit;
|
||||
} else {
|
||||
limit = limit || (offset ? 20 : (prerendered || 5));
|
||||
limit = limit;
|
||||
}
|
||||
|
||||
var history = historyStorage.history.slice(offset, offset + limit);
|
||||
@ -3051,9 +3142,6 @@ export class AppMessagesManager {
|
||||
});
|
||||
}
|
||||
|
||||
if(!backLimit && !limit) {
|
||||
limit = prerendered || 20;
|
||||
}
|
||||
if(offsetNotFound) {
|
||||
offset = 0;
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
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 = {
|
||||
_: 'pollAnswer',
|
||||
@ -58,24 +63,60 @@ export type Poll = {
|
||||
}>,
|
||||
rQuestion?: string,
|
||||
rReply?: string,
|
||||
chosenIndex?: number
|
||||
};
|
||||
|
||||
class AppPollsManager {
|
||||
private polls: {[id: string]: Poll} = {};
|
||||
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) {
|
||||
let id = poll.id;
|
||||
if(this.polls[id]) {
|
||||
this.results[id] = results;
|
||||
return;
|
||||
poll = this.polls[id];
|
||||
this.saveResults(poll, results);
|
||||
return poll;
|
||||
}
|
||||
|
||||
this.polls[id] = poll;
|
||||
this.results[id] = results;
|
||||
|
||||
poll.rQuestion = RichTextProcessor.wrapEmojiText(poll.question);
|
||||
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} {
|
||||
@ -84,6 +125,27 @@ class AppPollsManager {
|
||||
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();
|
2
src/lib/smoothscroll.js
Normal file
2
src/lib/smoothscroll.js
Normal 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()}();
|
@ -288,7 +288,7 @@ export function getSelectedText() {
|
||||
|
||||
export const $rootScope = {
|
||||
$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();
|
||||
let myCustomEvent = new CustomEvent(name, {detail});
|
||||
document.dispatchEvent(myCustomEvent);
|
||||
|
@ -5,6 +5,7 @@ import Config from '../lib/config';
|
||||
|
||||
import { findUpTag } from "../lib/utils";
|
||||
import pageAuthCode from "./pageAuthCode";
|
||||
import pageSignQR from './pageSignQR';
|
||||
//import apiManager from "../lib/mtproto/apiManager";
|
||||
import apiManager from "../lib/mtproto/mtprotoworker";
|
||||
import Page from "./page";
|
||||
@ -54,6 +55,10 @@ let onFirstMount = () => {
|
||||
|
||||
let initedSelect = false;
|
||||
|
||||
page.pageEl.querySelector('.a-qr').addEventListener('click', () => {
|
||||
pageSignQR.mount();
|
||||
});
|
||||
|
||||
selectCountryCode.addEventListener('focus', function(this: typeof selectCountryCode, e) {
|
||||
/* this.removeAttribute('readonly'); */
|
||||
if(!initedSelect) {
|
||||
@ -114,7 +119,7 @@ let onFirstMount = () => {
|
||||
parent.appendChild(wrapper);
|
||||
}/* , {once: true} */);
|
||||
selectCountryCode.addEventListener('blur', function(this: typeof selectCountryCode, e) {
|
||||
//parent.removeChild(wrapper);
|
||||
parent.removeChild(wrapper);
|
||||
e.cancelBubble = true;
|
||||
}, {capture: true});
|
||||
|
||||
|
@ -3,6 +3,7 @@ import apiManager from '../lib/mtproto/mtprotoworker';
|
||||
import Page from './page';
|
||||
import pageIm from './pageIm';
|
||||
import pagePassword from './pagePassword';
|
||||
import pageSignIn from './pageSignIn';
|
||||
import { App } from '../lib/mtproto/mtproto_config';
|
||||
import { bytesToBase64, bytesCmp } from '../lib/bin_utils';
|
||||
import serverTimeManager from '../lib/mtproto/serverTimeManager';
|
||||
@ -53,6 +54,10 @@ let onFirstMount = async() => {
|
||||
const pageElement = page.pageEl;
|
||||
const imageDiv = pageElement.querySelector('.auth-image') as HTMLDivElement;
|
||||
|
||||
page.pageEl.querySelector('.a-qr').addEventListener('click', () => {
|
||||
pageSignIn.mount();
|
||||
});
|
||||
|
||||
const results = await Promise.all([
|
||||
import('qr-code-styling' as any)
|
||||
]);
|
||||
|
@ -362,6 +362,11 @@ $time-background: rgba(0, 0, 0, 0.35);
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
i {
|
||||
font-style: normal;
|
||||
color: $color-blue;
|
||||
}
|
||||
|
||||
img.emoji {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
@ -6,6 +6,18 @@
|
||||
max-width: $chat-max-width;
|
||||
margin: 0 auto;
|
||||
|
||||
&.is-selected {
|
||||
&:before {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #7ca09f;
|
||||
content: " ";
|
||||
display: block;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-date {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
@ -106,7 +118,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-group-last) .user-avatar {
|
||||
&:not(.is-group-last) .bubble__container > .user-avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -188,7 +200,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.emoji-1x {
|
||||
&.emoji-1x .attachment {
|
||||
font-size: 96px;
|
||||
|
||||
img.emoji {
|
||||
@ -199,7 +211,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.emoji-2x {
|
||||
&.emoji-2x .attachment {
|
||||
font-size: 64px;
|
||||
|
||||
img.emoji {
|
||||
@ -210,7 +222,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.emoji-3x {
|
||||
&.emoji-3x .attachment {
|
||||
font-size: 52px;
|
||||
|
||||
img.emoji {
|
||||
@ -590,6 +602,10 @@
|
||||
content: "\e929";
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
.time {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.message.contact-message {
|
||||
@ -599,6 +615,7 @@
|
||||
.contact {
|
||||
display: flex;
|
||||
padding: 2px 0;
|
||||
cursor: pointer;
|
||||
|
||||
&-avatar {
|
||||
color: #fff;
|
||||
@ -639,6 +656,8 @@
|
||||
&-name {
|
||||
line-height: 1.4;
|
||||
margin-top: 1px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -844,15 +863,15 @@
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
img.emoji {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bubble-audio.is-in .time {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.bubble-audio.is-out .time {
|
||||
width: inherit;
|
||||
.bubble-audio .time {
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
.bubble.is-in {
|
||||
@ -959,6 +978,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* .poll {
|
||||
&-answer-selected {
|
||||
&:before {
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
.bubble.is-out {
|
||||
@ -1122,6 +1149,10 @@
|
||||
stroke: #4fae4e;
|
||||
}
|
||||
|
||||
&-answer-selected {
|
||||
background-color: #4fae4e;
|
||||
}
|
||||
|
||||
&-answer:hover {
|
||||
.animation-ring {
|
||||
background-color: rgba(79, 174, 78, 0.08);
|
||||
@ -1163,7 +1194,7 @@ poll-element {
|
||||
|
||||
&-text {
|
||||
margin-top: 7px;
|
||||
margin-left: 7px;
|
||||
margin-left: 14px;
|
||||
}
|
||||
|
||||
&-percents {
|
||||
@ -1174,7 +1205,26 @@ poll-element {
|
||||
font-weight: 500;
|
||||
margin-top: 7px;
|
||||
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 {
|
||||
@ -1182,7 +1232,9 @@ poll-element {
|
||||
visibility: visible;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-voting {
|
||||
.progress-ring__circle {
|
||||
stroke-dashoffset: -19.792;
|
||||
animation: pollAnswerRotate 0.65s linear infinite;
|
||||
@ -1197,13 +1249,16 @@ poll-element {
|
||||
}
|
||||
|
||||
&-line {
|
||||
height: 28px;
|
||||
height: 35px;
|
||||
position: absolute;
|
||||
left: 11.5px;
|
||||
top: 14px;
|
||||
left: 17.5px;
|
||||
top: 11px;
|
||||
transition: stroke-dashoffset .34s linear, stroke-dasharray .34s linear;
|
||||
stroke-dashoffset: 0;
|
||||
stroke-dasharray: 0, 485.9;
|
||||
|
||||
use {
|
||||
stroke-width: 3.5px;
|
||||
stroke-width: 4px;
|
||||
stroke-linecap: round;
|
||||
stroke: #50a2e9;
|
||||
fill: none;
|
||||
@ -1223,7 +1278,7 @@ poll-element {
|
||||
align-items: center;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
margin-left: -1px;
|
||||
margin-left: 5px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
@ -1256,6 +1311,7 @@ poll-element {
|
||||
stroke-dashoffset: 0;
|
||||
stroke-opacity: 1;
|
||||
stroke-width: 2;
|
||||
stroke: #8d969c;
|
||||
fill: transparent;
|
||||
}
|
||||
}
|
||||
@ -1268,6 +1324,20 @@ poll-element {
|
||||
.poll-answer-percents {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.poll-answer-selected {
|
||||
animation-direction: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-retracting {
|
||||
.circle-hover {
|
||||
transition-delay: .24s;
|
||||
}
|
||||
|
||||
.animation-ring {
|
||||
transition-delay: .22s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,10 +102,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* li.dialog-pinned + .pinned-delimiter {
|
||||
display: flex;
|
||||
} */
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
|
@ -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 {
|
||||
.auth-image {
|
||||
position: relative;
|
||||
|
Loading…
x
Reference in New Issue
Block a user