Browse Source

added more emoji's & coloring animated emoji stickers & dates chat fix

master
morethanwords 4 years ago
parent
commit
f903f8a1c7
  1. 9
      src/components/chatInput.ts
  2. 14
      src/components/emoticonsDropdown.ts
  3. 2
      src/components/poll.ts
  4. 5
      src/components/preloader.ts
  5. 7
      src/components/scrollable_new.ts
  6. 76
      src/components/stickyIntersector.ts
  7. 81
      src/components/wrappers.ts
  8. 2
      src/emoji.json
  9. 20
      src/format_jsons.js
  10. 2
      src/lib/appManagers/appDialogsManager.ts
  11. 222
      src/lib/appManagers/appImManager.ts
  12. 64
      src/lib/appManagers/appMediaViewer.ts
  13. 234
      src/lib/appManagers/appMessagesManager.ts
  14. 7
      src/lib/appManagers/appSidebarRight.ts
  15. 2
      src/lib/appManagers/appStickersManager.ts
  16. 2
      src/lib/config.ts
  17. 76
      src/lib/lottieLoader.ts
  18. 27
      src/lib/utils.js
  19. 45
      src/scss/partials/_chatBubble.scss
  20. 8
      src/scss/style.scss

9
src/components/chatInput.ts

@ -447,6 +447,7 @@ export class ChatInput { @@ -447,6 +447,7 @@ export class ChatInput {
}
});
let emoticonsDisplayTimeout = 0;
this.toggleEmoticons.onmouseover = (e) => {
clearTimeout(this.emoticonsTimeout);
this.emoticonsTimeout = setTimeout(() => {
@ -464,6 +465,11 @@ export class ChatInput { @@ -464,6 +465,11 @@ export class ChatInput {
this.toggleEmoticons.classList.remove('active');
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
this.emoticonsLazyLoadQueue.lock();
clearTimeout(emoticonsDisplayTimeout);
emoticonsDisplayTimeout = setTimeout(() => {
this.emoticonsDropdown.style.display = 'none';
}, 200);
}, 200);
};
@ -471,8 +477,11 @@ export class ChatInput { @@ -471,8 +477,11 @@ export class ChatInput {
clearTimeout(this.emoticonsTimeout);
};
} else {
this.emoticonsDropdown.style.display = '';
void this.emoticonsDropdown.offsetLeft; // reflow
this.emoticonsDropdown.classList.add('active');
this.emoticonsLazyLoadQueue.unlock();
clearTimeout(emoticonsDisplayTimeout);
}
this.toggleEmoticons.classList.add('active');

14
src/components/emoticonsDropdown.ts

@ -290,7 +290,13 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -290,7 +290,13 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
docs.forEach(doc => {
let div = document.createElement('div');
wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true, false, true);
wrapSticker({
doc,
div,
lazyLoadQueue,
group: EMOTICONSSTICKERGROUP,
onlyThumb: true
});
itemsDiv.append(div);
});
@ -408,7 +414,11 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -408,7 +414,11 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}
});
} else { // as thumb will be used first sticker
wrapSticker(stickerSet.documents[0], li as any, undefined, undefined, EMOTICONSSTICKERGROUP); // kostil
wrapSticker({
doc: stickerSet.documents[0],
div: li as any,
group: EMOTICONSSTICKERGROUP
}); // kostil
}
categoryPush(categoryDiv, stickerSet.set.title, stickerSet.documents, false);

2
src/components/poll.ts

@ -128,10 +128,10 @@ export default class PollElement extends HTMLElement { @@ -128,10 +128,10 @@ export default class PollElement extends HTMLElement {
</svg>
</div>
<div class="poll-answer-percents"></div>
<div class="poll-answer-text">${RichTextProcessor.wrapEmojiText(answer.text)}</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('');

5
src/components/preloader.ts

@ -44,7 +44,7 @@ export default class ProgressivePreloader { @@ -44,7 +44,7 @@ export default class ProgressivePreloader {
}
}
public attach(elem: Element, reset = true, promise?: CancellablePromise<any>) {
public attach(elem: Element, reset = true, promise?: CancellablePromise<any>, append = true) {
if(promise) {
this.promise = promise;
@ -75,7 +75,8 @@ export default class ProgressivePreloader { @@ -75,7 +75,8 @@ export default class ProgressivePreloader {
window.requestAnimationFrame(() => {
if(this.detached) return;
this.detached = false;
elem.append(this.preloader);
elem[append ? 'append' : 'prepend'](this.preloader);
});
/* let isIn = isInDOM(this.preloader);

7
src/components/scrollable_new.ts

@ -364,7 +364,7 @@ export default class Scrollable { @@ -364,7 +364,7 @@ export default class Scrollable {
public scrollIntoView(element: HTMLElement, smooth = true) {
if(element.parentElement && !this.scrollLocked) {
let isFirstUnread = element.classList.contains('is-first-unread');
let offsetTop = element.offsetTop;
let offsetTop = element.getBoundingClientRect().top - this.container.getBoundingClientRect().top;
if(!smooth && isFirstUnread) {
this.scrollTo(offsetTop, false);
return;
@ -372,8 +372,9 @@ export default class Scrollable { @@ -372,8 +372,9 @@ export default class Scrollable {
let clientHeight = this.container.clientHeight;
let height = element.scrollHeight;
offsetTop -= (clientHeight - height) / 2;
let d = (clientHeight - height) / 2;
offsetTop = this.container.scrollTop + offsetTop - d;
this.scrollTo(offsetTop, smooth);
}

76
src/components/stickyIntersector.ts

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
export default class StickyIntersector {
private headersObserver: IntersectionObserver;
private elementsObserver: IntersectionObserver;
constructor(private container: HTMLElement, private handler: (stuck: boolean, target: HTMLElement) => void) {
this.observeHeaders();
this.observeElements();
}
/**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--top` become visible/invisible at the top of the container.
* @param {!Element} container
*/
private observeHeaders() {
this.headersObserver = new IntersectionObserver((entries) => {
for(const entry of entries) {
const targetInfo = entry.boundingClientRect;
const stickyTarget = entry.target.parentElement;
const rootBoundsInfo = entry.rootBounds;
// Started sticking.
if(targetInfo.bottom < rootBoundsInfo.top) {
this.handler(true, stickyTarget);
}
// Stopped sticking.
if(targetInfo.bottom >= rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
this.handler(false, stickyTarget);
}
}
}, {threshold: 0, root: this.container});
}
private observeElements() {
this.elementsObserver = new IntersectionObserver((entries) => {
let entry = entries.filter(entry => entry.boundingClientRect.top < 0).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];
if(!entry) return;
let container = entry.isIntersecting ? entry.target : entry.target.nextElementSibling;
this.handler(true, container as HTMLElement);
}, {root: this.container});
}
/**
* @param {!Element} container
* @param {string} className
*/
private addSentinel(container: HTMLElement, className: string) {
const sentinel = document.createElement('div');
sentinel.classList.add('sticky_sentinel', className);
return container.appendChild(sentinel);
}
/**
* Notifies when elements w/ the `sticky` class begin to stick or stop sticking.
* Note: the elements should be children of `container`.
* @param {!Element} container
*/
public observeStickyHeaderChanges(element: HTMLElement) {
const headerSentinel = this.addSentinel(element, 'sticky_sentinel--top');
this.headersObserver.observe(headerSentinel);
this.elementsObserver.observe(element);
}
public disconnect() {
this.headersObserver.disconnect();
this.elementsObserver.disconnect();
}
public unobserve(element: HTMLElement, headerSentinel: HTMLElement) {
this.elementsObserver.unobserve(element);
this.headersObserver.unobserve(headerSentinel);
}
}

81
src/components/wrappers.ts

@ -4,7 +4,7 @@ import apiManager from '../lib/mtproto/mtprotoworker'; @@ -4,7 +4,7 @@ import apiManager from '../lib/mtproto/mtprotoworker';
import LottieLoader from '../lib/lottieLoader';
import appStickersManager from "../lib/appManagers/appStickersManager";
import appDocsManager from "../lib/appManagers/appDocsManager";
import { formatBytes } from "../lib/utils";
import { formatBytes, getEmojiToneIndex } from "../lib/utils";
import ProgressivePreloader from './preloader';
import LazyLoadQueue from './lazyLoadQueue';
import apiFileManager from '../lib/mtproto/apiFileManager';
@ -108,7 +108,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -108,7 +108,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
container.append(video);
}
let span: HTMLSpanElement;
let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
if(doc.type != 'round') {
span = document.createElement('span');
span.classList.add('video-time');
@ -117,7 +117,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -117,7 +117,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
if(doc.type != 'gif') {
span.innerText = (doc.duration + '').toHHMMSS(false);
let spanPlay = document.createElement('span');
spanPlay = document.createElement('span');
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
container.append(spanPlay);
} else {
@ -127,11 +127,11 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -127,11 +127,11 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
let loadVideo = async() => {
if(message.media.preloader) { // means upload
message.media.preloader.attach(container);
(message.media.preloader as ProgressivePreloader).attach(container, undefined, undefined, false);
} else if(!doc.downloaded) {
let preloader = new ProgressivePreloader(container, true);
let promise = appDocsManager.downloadDoc(doc);
preloader.attach(container, true, promise);
preloader.attach(container, true, promise, false);
await promise;
}
@ -684,7 +684,16 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme @@ -684,7 +684,16 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme
return photo.downloaded ? load() : lazyLoadQueue.push({div: container, load: load, wasSeen: true});
}
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false, onlyThumb = false) {
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji}: {
doc: MTDocument,
div: HTMLDivElement,
middleware?: () => boolean,
lazyLoadQueue?: LazyLoadQueue,
group?: string,
play?: boolean,
onlyThumb?: boolean,
emoji?: string
}) {
let stickerType = doc.sticker;
if(stickerType == 2 && !LottieLoader.loaded) {
@ -699,6 +708,8 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -699,6 +708,8 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
div.dataset.docID = doc.id;
//console.log('wrap sticker', doc, div, onlyThumb);
const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1;
if(doc.thumbs && !div.firstElementChild && (!doc.downloaded || stickerType == 2)) {
let thumb = doc.thumbs[0];
@ -777,52 +788,52 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -777,52 +788,52 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
const reader = new FileReader();
reader.addEventListener('loadend', async(e) => {
console.time('decompress sticker' + doc.id);
console.time('render sticker' + doc.id);
//console.time('decompress sticker' + doc.id);
//console.time('render sticker' + doc.id);
// @ts-ignore
const text = e.srcElement.result;
let json = await apiManager.gzipUncompress<string>(text, true);
console.timeEnd('decompress sticker' + doc.id);
//console.timeEnd('decompress sticker' + doc.id);
console.log('sticker json:', json);
let animation = await LottieLoader.loadAnimation({
container: div,
loop: false,
autoplay: false,
animationData: JSON.parse(json),
renderer: canvas ? 'canvas' : 'svg'
}, group);
renderer: 'svg'
}, group, toneIndex);
console.timeEnd('render sticker' + doc.id);
//console.timeEnd('render sticker' + doc.id);
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') {
div.firstElementChild.remove();
}
if(!canvas) {
div.addEventListener('mouseover', (e) => {
let animation = LottieLoader.getAnimation(div, group);
div.addEventListener('mouseover', (e) => {
let animation = LottieLoader.getAnimation(div, group);
if(animation) {
//console.log('sticker hover', animation, div);
if(animation) {
//console.log('sticker hover', animation, div);
// @ts-ignore
animation.loop = true;
// @ts-ignore
if(animation.currentFrame == animation.totalFrames - 1) {
animation.goToAndPlay(0, true);
} else {
animation.play();
}
div.addEventListener('mouseout', () => {
// @ts-ignore
animation.loop = false;
}, {once: true});
// @ts-ignore
animation.loop = true;
// @ts-ignore
if(animation.currentFrame == animation.totalFrames - 1) {
animation.goToAndPlay(0, true);
} else {
animation.play();
}
});
}
div.addEventListener('mouseout', () => {
// @ts-ignore
animation.loop = false;
}, {once: true});
}
});
if(play) {
animation.play();

2
src/emoji.json

File diff suppressed because one or more lines are too long

20
src/format_jsons.js

@ -48,6 +48,7 @@ if(false) { @@ -48,6 +48,7 @@ if(false) {
{
let categories = {
"Smileys & Emotion": 1
, "People & Body": 1
, "Animals & Nature": 2
, "Food & Drink": 3
, "Travel & Places": 4
@ -58,18 +59,23 @@ if(false) { @@ -58,18 +59,23 @@ if(false) {
, "Skin Tones": 8
};
let concatCategories = [['Objects', 'Symbols'], ['Smileys & Emotion', 'People & Body']];
let maxIndexes = {};
let maxObjectsIndex = -1;
formatted.forEach(e => {
if(e.category == 'Objects') {
if(e.sort_order > maxObjectsIndex) {
maxObjectsIndex = e.sort_order;
}
if(concatCategories.findIndex(c => c[0] == e.category) === -1) return;
if(!maxIndexes.hasOwnProperty(e.category)) maxIndexes[e.category] = 0;
if(e.sort_order > maxIndexes[e.category]) {
maxIndexes[e.category] = e.sort_order;
}
});
formatted.forEach(e => {
if(e.category == 'Symbols') {
e.sort_order += maxObjectsIndex;
}
let concatDetails = concatCategories.find(c => c[1] == e.category);
if(!concatDetails) return;
e.sort_order += maxIndexes[concatDetails[0]];
});
formatted.forEach(e => {

2
src/lib/appManagers/appDialogsManager.ts

@ -649,7 +649,7 @@ export class AppDialogsManager { @@ -649,7 +649,7 @@ export class AppDialogsManager {
if(onFound) onFound();
let peerID = +elem.getAttribute('data-peerID');
let lastMsgID = +elem.dataset.mid || 0;
let lastMsgID = +elem.dataset.mid || undefined;
if(!samePeer) {
elem.classList.add('active');

222
src/lib/appManagers/appImManager.ts

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
import apiManager from '../mtproto/mtprotoworker';
import { $rootScope, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, findUpTag, langPack, whichChild } from "../utils";
import appUsersManager from "./appUsersManager";
import appMessagesManager from "./appMessagesManager";
import appMessagesManager, { Dialog } from "./appMessagesManager";
import appPeersManager from "./appPeersManager";
import appProfileManager from "./appProfileManager";
import appDialogsManager from "./appDialogsManager";
@ -30,6 +30,7 @@ import appForward from '../../components/appForward'; @@ -30,6 +30,7 @@ import appForward from '../../components/appForward';
import appStickersManager from './appStickersManager';
import AvatarElement from '../../components/avatar';
import appInlineBotsManager from './AppInlineBotsManager';
import StickyIntersector from '../../components/stickyIntersector';
console.log('appImManager included!');
@ -37,6 +38,8 @@ appSidebarLeft; // just to include @@ -37,6 +38,8 @@ appSidebarLeft; // just to include
let testScroll = false;
const IGNOREACTIONS = ['messageActionChannelMigrateFrom'];
export class AppImManager {
public pageEl = document.getElementById('page-chats') as HTMLDivElement;
public btnMute = this.pageEl.querySelector('.tool-mute') as HTMLButtonElement;
@ -111,10 +114,8 @@ export class AppImManager { @@ -111,10 +114,8 @@ export class AppImManager {
private onScrollRAF = 0;
private isScrollingTimeout = 0;
private datesIntersectionObserver: IntersectionObserver = null;
private lastDateMessageDiv: HTMLDivElement = null;
private unreadedObserver: IntersectionObserver = null;
private unreaded: number[] = [];
private loadedTopTimes = 0;
private loadedBottomTimes = 0;
@ -126,6 +127,8 @@ export class AppImManager { @@ -126,6 +127,8 @@ export class AppImManager {
private peerChanged: boolean;
private firstUnreadBubble: HTMLDivElement = null;
private stickyIntersector: StickyIntersector = null;
constructor() {
/* if(!lottieLoader.loaded) {
lottieLoader.loadLottie();
@ -156,10 +159,7 @@ export class AppImManager { @@ -156,10 +159,7 @@ export class AppImManager {
let details = e.detail;
if(!this.scrolledAllDown) {
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
if(dialog) {
this.setPeer(this.peerID, dialog.top_message);
}
this.setPeer(this.peerID, 0);
} else {
this.renderNewMessagesByIDs([details.messageID], true);
}
@ -393,9 +393,7 @@ export class AppImManager { @@ -393,9 +393,7 @@ export class AppImManager {
}).sort((a, b) => a - b);
ids.forEach(id => {
let bubble = this.bubbles[id];
let elements = this.bubbles[id].querySelectorAll('.attachment img, .preview img, video, .bubble__media-container') as NodeListOf<HTMLElement>;
let elements = this.bubbles[id].querySelectorAll('.album-item img, .album-item video, .preview img, .preview video, .bubble__media-container') as NodeListOf<HTMLElement>;
Array.from(elements).forEach((element: HTMLElement) => {
let albumItem = findUpClassName(element, 'album-item');
targets.push({
@ -424,7 +422,7 @@ export class AppImManager { @@ -424,7 +422,7 @@ export class AppImManager {
let peerID = +splitted[0];
let msgID = +splitted[1];
////this.log('savedFrom', peerID, msgID);
this.setPeer(peerID, msgID/* , true */);
this.setPeer(peerID, msgID);
return;
} else if(target.tagName == "AVATAR-ELEMENT" || target.classList.contains('name')) {
let peerID = +target.dataset.peerID;
@ -650,25 +648,15 @@ export class AppImManager { @@ -650,25 +648,15 @@ export class AppImManager {
this.setScroll();
//apiUpdatesManager.attach();
this.datesIntersectionObserver = new IntersectionObserver((entries) => {
//this.log('intersection', entries);
let entry = entries.filter(entry => entry.boundingClientRect.top < 0).sort((a, b) => b.boundingClientRect.top - a.boundingClientRect.top)[0];
if(!entry) return;
let container = entry.isIntersecting ? entry.target : entry.target.nextElementSibling;
this.stickyIntersector = new StickyIntersector(this.scrollable.container, (stuck, target) => {
for(let timestamp in this.dateMessages) {
let dateMessage = this.dateMessages[timestamp];
if(dateMessage.container == container) {
if(this.lastDateMessageDiv) {
this.lastDateMessageDiv.classList.remove('is-sticky');
}
dateMessage.div.classList.add('is-sticky');
this.lastDateMessageDiv = dateMessage.div;
if(dateMessage.container == target) {
dateMessage.div.classList.toggle('is-sticky', stuck);
break;
}
}
}/* , {root: this.chatInner} */);
});
this.unreadedObserver = new IntersectionObserver((entries) => {
let readed: number[] = [];
@ -679,22 +667,32 @@ export class AppImManager { @@ -679,22 +667,32 @@ export class AppImManager {
let mid = +target.dataset.mid;
readed.push(mid);
this.unreadedObserver.unobserve(target);
this.unreaded.findAndSplice(id => id == mid);
}
});
if(readed.length) {
let max = Math.max(...readed);
let min = Math.min(...readed);
if(this.peerID < 0) {
max = appMessagesIDsManager.getMessageIDInfo(max)[0];
min = appMessagesIDsManager.getMessageIDInfo(min)[0];
let length = readed.length;
for(let i = this.unreaded.length - 1; i >= 0; --i) {
let mid = this.unreaded[i];
if(mid < max) {
length++;
this.unreaded.splice(i, 1);
}
}
this.log('will readHistory by ids:', max, length);
/* if(this.peerID < 0) {
max = appMessagesIDsManager.getMessageIDInfo(max)[0];
} */
//appMessagesManager.readMessages(readed);
false && appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
/* false && */appMessagesManager.readHistory(this.peerID, max, length).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, min);
appMessagesManager.readHistory(this.peerID, max, length);
});
}
});
@ -788,7 +786,7 @@ export class AppImManager { @@ -788,7 +786,7 @@ export class AppImManager {
if(this.isScrollingTimeout) {
clearTimeout(this.isScrollingTimeout);
} else if(this.chatInner.classList.contains('is-scrolling')) {
} else if(!this.chatInner.classList.contains('is-scrolling')) {
this.chatInner.classList.add('is-scrolling');
}
@ -927,19 +925,19 @@ export class AppImManager { @@ -927,19 +925,19 @@ export class AppImManager {
this.getHistoryTopPromise = this.getHistoryBottomPromise = undefined;
this.datesIntersectionObserver.disconnect();
this.lastDateMessageDiv = null;
this.stickyIntersector.disconnect();
this.unreadedObserver.disconnect();
this.unreaded.length = 0;
this.loadedTopTimes = this.loadedBottomTimes = 0;
////console.timeEnd('appImManager cleanup');
}
public setPeer(peerID: number, lastMsgID = 0) {
console.time('appImManager setPeer');
console.time('appImManager setPeer pre promise');
public setPeer(peerID: number, lastMsgID?: number) {
//console.time('appImManager setPeer');
//console.time('appImManager setPeer pre promise');
////console.time('appImManager: pre render start');
if(peerID == 0) {
appSidebarRight.toggleSidebar(false);
@ -950,23 +948,27 @@ export class AppImManager { @@ -950,23 +948,27 @@ export class AppImManager {
return false;
}
let samePeer = this.peerID == peerID;
const samePeer = this.peerID == peerID;
if(this.setPeerPromise && samePeer) return this.setPeerPromise;
/* if(lastMsgID) {
appMessagesManager.readHistory(peerID, lastMsgID); // lol
} */
const dialog = appMessagesManager.getDialogByPeerID(peerID)[0] || null;
const topMessage = lastMsgID <= 0 ? lastMsgID : dialog?.top_message ?? 0;
if(lastMsgID === undefined && dialog) {
if(dialog.unread_count) {
lastMsgID = dialog.read_inbox_max_id;
} else {
lastMsgID = dialog.top_message;
}
}
if(samePeer) {
if(!testScroll && !lastMsgID) {
return true;
}
if(this.bubbles[lastMsgID]) {
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog && lastMsgID == dialog.top_message) {
if(dialog && lastMsgID == topMessage) {
this.log('will scroll down', this.scroll.scrollTop, this.scroll.scrollHeight);
this.scroll.scrollTop = this.scroll.scrollHeight;
} else {
@ -983,19 +985,12 @@ export class AppImManager { @@ -983,19 +985,12 @@ export class AppImManager {
// set new
this.peerID = $rootScope.selectedPeerID = peerID;
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0] || null;
if(!lastMsgID && dialog) {
if(dialog.unread_count) {
lastMsgID = dialog.read_inbox_max_id;
} else {
lastMsgID = dialog.top_message;
}
}
//////this.log('setPeer peerID:', this.peerID, dialog, lastMsgID);
this.log('setPeer peerID:', this.peerID, dialog, lastMsgID, topMessage);
const isJump = lastMsgID != dialog?.top_message;
const isJump = lastMsgID != topMessage;
// add last message, bc in getHistory will load < max_id
const additionMsgID = isJump ? 0 : dialog.top_message;
const additionMsgID = isJump ? 0 : topMessage;
/* this.setPeerPromise = null;
this.preloader.detach();
@ -1005,28 +1000,33 @@ export class AppImManager { @@ -1005,28 +1000,33 @@ export class AppImManager {
const maxBubbleID = samePeer && Math.max(...Object.keys(this.bubbles).map(mid => +mid));
//let oldChatInner = this.chatInner;
const oldChatInner = this.chatInner;
this.cleanup();
this.chatInner = document.createElement('div');
this.chatInner.id = 'bubbles-inner';
this.scrollable.appendTo = this.chatInner;
this.chatInner.className = oldChatInner.className;
this.chatInner.classList.add('disable-hover', 'is-scrolling');
this.lazyLoadQueue.lock();
let {promise, cached} = this.getHistory(lastMsgID, true, isJump, additionMsgID);
const {promise, cached} = this.getHistory(lastMsgID, true, isJump, additionMsgID);
appSidebarRight.setPeer(this.peerID);
if(!samePeer) {
appSidebarRight.setPeer(this.peerID);
} else {
this.peerChanged = true;
}
// clear
if(!cached) {
this.scrollable.container.innerHTML = '';
//oldChatInner.remove();
this.finishPeerChange();
!samePeer && this.finishPeerChange();
this.preloader.attach(this.bubblesContainer);
}
console.timeEnd('appImManager setPeer pre promise');
//console.timeEnd('appImManager setPeer pre promise');
this.setPeerPromise = Promise.all([
promise.then(() => {
@ -1035,7 +1035,7 @@ export class AppImManager { @@ -1035,7 +1035,7 @@ export class AppImManager {
if(cached) {
this.scrollable.container.innerHTML = '';
//oldChatInner.remove();
this.finishPeerChange();
!samePeer && this.finishPeerChange();
} else {
this.preloader.detach();
}
@ -1046,19 +1046,19 @@ export class AppImManager { @@ -1046,19 +1046,19 @@ export class AppImManager {
this.lazyLoadQueue.unlock();
if(dialog && lastMsgID && lastMsgID != dialog.top_message && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) {
if(dialog && lastMsgID && lastMsgID != topMessage && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) {
if(this.scrollable.scrollLocked) {
clearTimeout(this.scrollable.scrollLocked);
this.scrollable.scrollLocked = 0;
}
let fromUp = maxBubbleID > 0 && maxBubbleID < lastMsgID;
const fromUp = maxBubbleID > 0 && (maxBubbleID < lastMsgID || lastMsgID < 0);
if(!fromUp && samePeer) {
this.scrollable.scrollTop = this.scrollable.scrollHeight;
}
let forwardingUnread = dialog.read_inbox_max_id == lastMsgID;
let bubble = forwardingUnread ? (this.firstUnreadBubble || this.bubbles[lastMsgID]) : this.bubbles[lastMsgID];
const forwardingUnread = dialog.read_inbox_max_id == lastMsgID;
const bubble = forwardingUnread ? (this.firstUnreadBubble || this.bubbles[lastMsgID]) : this.bubbles[lastMsgID];
this.scrollable.scrollIntoView(bubble, samePeer/* , fromUp */);
if(!forwardingUnread) {
@ -1069,13 +1069,13 @@ export class AppImManager { @@ -1069,13 +1069,13 @@ export class AppImManager {
}
// warning
if(!lastMsgID || (dialog && (this.bubbles[dialog.top_message] || lastMsgID == dialog.top_message))) {
if(!lastMsgID || this.bubbles[topMessage] || lastMsgID == topMessage) {
this.scrolledAllDown = true;
}
this.log('scrolledAllDown:', this.scrolledAllDown);
console.timeEnd('appImManager setPeer');
//console.timeEnd('appImManager setPeer');
return true;
}).catch(err => {
@ -1115,17 +1115,14 @@ export class AppImManager { @@ -1115,17 +1115,14 @@ export class AppImManager {
const isChannel = appPeersManager.isChannel(peerID);
const hasRights = isChannel && appChatsManager.hasRights(-peerID, 'send');
if(hasRights) this.chatInner.classList.add('has-rights');
else this.chatInner.classList.remove('has-rights');
this.chatInner.classList.toggle('has-rights', hasRights);
this.chatInput.style.display = !isChannel || hasRights ? '' : 'none';
this.topbar.style.display = '';
if(appPeersManager.isAnyGroup(peerID) || peerID == this.myID) this.chatInner.classList.add('is-chat');
else this.chatInner.classList.remove('is-chat');
if(isChannel) this.chatInner.classList.add('is-channel');
else this.chatInner.classList.remove('is-channel');
this.chatInner.classList.toggle('is-chat', appPeersManager.isAnyGroup(peerID) || peerID == this.myID);
this.chatInner.classList.toggle('is-channel', isChannel);
this.pinnedMessageContainer.style.display = 'none';
@ -1159,7 +1156,7 @@ export class AppImManager { @@ -1159,7 +1156,7 @@ export class AppImManager {
}) as Promise<boolean>;
}
public updateUnreadByDialog(dialog: any) {
public updateUnreadByDialog(dialog: Dialog) {
let maxID = this.peerID == this.myID ? dialog.read_inbox_max_id : dialog.read_outbox_max_id;
///////this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut);
@ -1190,6 +1187,7 @@ export class AppImManager { @@ -1190,6 +1187,7 @@ export class AppImManager {
this.bubbleGroups.removeBubble(bubble, id);
this.unreadedObserver.unobserve(bubble);
//this.unreaded.findAndSplice(mid => mid == id);
this.scrollable.removeElement(bubble);
//bubble.remove();
});
@ -1290,7 +1288,7 @@ export class AppImManager { @@ -1290,7 +1288,7 @@ export class AppImManager {
this.scrollable.append(container, false);
}
this.datesIntersectionObserver.observe(container);
this.stickyIntersector.observeStickyHeaderChanges(container);
}
return this.dateMessages[dateTimestamp];
@ -1423,28 +1421,37 @@ export class AppImManager { @@ -1423,28 +1421,37 @@ export class AppImManager {
bubble.dataset.mid = message.mid;
if(message._ == 'messageService') {
bubble.className = 'bubble service';
let action = message.action;
let _ = action._;
if(IGNOREACTIONS.indexOf(_) !== -1) {
return bubble;
}
bubble.className = 'bubble service';
let title = appPeersManager.getPeerTitle(message.fromID);
let name = document.createElement('div');
name.classList.add('name');
name.dataset.peerID = message.fromID;
name.innerHTML = title;
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
}
// @ts-ignore
let l = langPack[_];
if(!l) {
l = '[' + _ + ']';
let str = '';
if(action.message) {
str = RichTextProcessor.wrapRichText(action.message, {noLinebreaks: true});
} else {
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
}
// @ts-ignore
let l = langPack[_];
if(!l) {
l = '[' + _ + ']';
}
str = l[0].toUpperCase() == l[0] ? l : (name.innerText ? name.outerHTML + ' ' : '') + l;
}
let str = l[0].toUpperCase() == l[0] ? l : (name.innerText ? name.outerHTML + ' ' : '') + l;
bubbleContainer.innerHTML = `<div class="service-msg">${str}</div>`;
if(updatePosition) {
@ -1630,6 +1637,9 @@ export class AppImManager { @@ -1630,6 +1637,9 @@ export class AppImManager {
//this.log('not our message', message, message.pFlags.unread);
if(message.pFlags.unread) {
this.unreadedObserver.observe(bubble);
if(!this.unreaded.indexOf(message.mid)) {
this.unreaded.push(message.mid);
}
}
}
@ -1845,19 +1855,28 @@ export class AppImManager { @@ -1845,19 +1855,28 @@ export class AppImManager {
bubble.classList.add('sticker-animated');
}
appPhotosManager.setAttachmentSize(doc, attachmentDiv, undefined, undefined, true);
let size = bubble.classList.contains('emoji-big') ? 140 : 200;
appPhotosManager.setAttachmentSize(doc, attachmentDiv, size, size, true);
//let preloader = new ProgressivePreloader(attachmentDiv, false);
bubbleContainer.style.height = attachmentDiv.style.height;
bubbleContainer.style.width = attachmentDiv.style.width;
//appPhotosManager.setAttachmentSize(doc, bubble);
wrapSticker(doc, attachmentDiv, () => {
if(this.peerID != peerID) {
this.log.warn('peer changed, canceling sticker attach');
return false;
}
return true;
}, this.lazyLoadQueue, 'chat', false, !!message.pending || !multipleRender);
wrapSticker({
doc,
div: attachmentDiv,
middleware: () => {
if(this.peerID != peerID) {
this.log.warn('peer changed, canceling sticker attach');
return false;
}
return true;
},
lazyLoadQueue: this.lazyLoadQueue,
group: 'chat',
play: !!message.pending || !multipleRender,
emoji: bubble.classList.contains('emoji-big') ? messageMessage : undefined
});
break;
} else if(doc.type == 'video' || doc.type == 'gif' || doc.type == 'round'/* && doc.size <= 20e6 */) {
@ -2215,6 +2234,7 @@ export class AppImManager { @@ -2215,6 +2234,7 @@ export class AppImManager {
return false;
});
} else {
this.log('getHistory result by maxID:', maxID, reverse, isBackLimit, result);
cached = true;
promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID);
//return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
@ -2295,7 +2315,7 @@ export class AppImManager { @@ -2295,7 +2315,7 @@ export class AppImManager {
if(dateMessage.container.childElementCount == 1) { // only date div
dateMessage.container.remove();
this.datesIntersectionObserver.unobserve(dateMessage.container);
this.stickyIntersector.unobserve(dateMessage.container, dateMessage.div);
delete this.dateMessages[i];
}
}

64
src/lib/appManagers/appMediaViewer.ts

@ -582,10 +582,10 @@ export class AppMediaViewer { @@ -582,10 +582,10 @@ export class AppMediaViewer {
public openMedia(message: any, target?: HTMLElement, reverse = false, targetContainer?: HTMLElement,
prevTargets: AppMediaViewer['prevTargets'] = [], nextTargets: AppMediaViewer['prevTargets'] = [], needLoadMore = true) {
////////this.log('openMedia doc:', message, prevTarget, nextTarget);
let media = message.media.photo || message.media.document || message.media.webpage.document || message.media.webpage.photo;
const media = message.media.photo || message.media.document || message.media.webpage.document || message.media.webpage.photo;
let isVideo = media.mime_type == 'video/mp4';
let isFirstOpen = !this.peerID;
const isVideo = media.mime_type == 'video/mp4';
const isFirstOpen = !this.peerID;
if(isFirstOpen) {
this.peerID = $rootScope.selectedPeerID;
@ -614,8 +614,8 @@ export class AppMediaViewer { @@ -614,8 +614,8 @@ export class AppMediaViewer {
this.buttons.prev.style.display = this.prevTargets.length ? '' : 'none';
this.buttons.next.style.display = this.nextTargets.length ? '' : 'none';
let container = this.content.container;
let useContainerAsTarget = !target;
const container = this.content.container;
const useContainerAsTarget = !target;
if(useContainerAsTarget) target = container;
this.currentMessageID = message.mid;
@ -635,13 +635,13 @@ export class AppMediaViewer { @@ -635,13 +635,13 @@ export class AppMediaViewer {
container.innerHTML = '';
}
let date = new Date(media.date * 1000);
let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const date = new Date(media.date * 1000);
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
let dateStr = months[date.getMonth()] + ' ' + date.getDate() + ' at '+ date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
const dateStr = months[date.getMonth()] + ' ' + date.getDate() + ' at '+ date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
this.author.date.innerText = dateStr;
let name = appPeersManager.getPeerTitle(message.fromID);
const name = appPeersManager.getPeerTitle(message.fromID);
this.author.nameEl.innerHTML = name;
if(message.message) {
@ -656,7 +656,7 @@ export class AppMediaViewer { @@ -656,7 +656,7 @@ export class AppMediaViewer {
// ok set
let wasActive = fromRight !== 0;
const wasActive = fromRight !== 0;
if(wasActive) {
this.moveTheMover(this.content.mover, fromRight === 1);
this.setNewMover();
@ -667,18 +667,19 @@ export class AppMediaViewer { @@ -667,18 +667,19 @@ export class AppMediaViewer {
////////this.log('wasActive:', wasActive);
const mover = this.content.mover;
//const maxWidth = appPhotosManager.windowW - 16;
const maxWidth = this.pageEl.scrollWidth - 16;
const maxHeight = appPhotosManager.windowH - 100;
const size = appPhotosManager.setAttachmentSize(isVideo ? media : media.id, container, maxWidth, maxHeight);
// need after setAttachmentSize
if(useContainerAsTarget) {
target = target.querySelector('img, video') || target;
}
let mover = this.content.mover;
//let maxWidth = appPhotosManager.windowW - 16;
let maxWidth = this.pageEl.scrollWidth - 16;
let maxHeight = appPhotosManager.windowH - 100;
if(isVideo) {
appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight);
////////this.log('will wrap video', media, size);
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
@ -721,10 +722,10 @@ export class AppMediaViewer { @@ -721,10 +722,10 @@ export class AppMediaViewer {
this.updateMediaSource(mover, url, 'source');
this.updateMediaSource(target, url, 'source');
} else {
let aspecter = mover.firstElementChild;
let img = aspecter.firstElementChild;
if(img instanceof HTMLImageElement) {
img.remove();
let div = mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = div.firstElementChild as HTMLImageElement;
if(image instanceof HTMLImageElement) {
image.remove();
}
renderImageFromUrl(source, url);
@ -735,11 +736,7 @@ export class AppMediaViewer { @@ -735,11 +736,7 @@ export class AppMediaViewer {
}
if(!video.parentElement) {
if(aspecter.classList.contains('media-viewer-aspecter')) {
aspecter.prepend(video);
} else {
mover.prepend(video);
}
div.prepend(video);
}
}
@ -748,8 +745,6 @@ export class AppMediaViewer { @@ -748,8 +745,6 @@ export class AppMediaViewer {
} else createPlayer();
}, 0);
} else {
let size = appPhotosManager.setAttachmentSize(media.id, container, maxWidth, maxHeight);
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
//return; // set and don't move
//if(wasActive) return;
@ -772,14 +767,17 @@ export class AppMediaViewer { @@ -772,14 +767,17 @@ export class AppMediaViewer {
this.updateMediaSource(target, url, 'img');
this.updateMediaSource(mover, url, 'img');
} else {
let aspecter = mover.firstElementChild;
let image = aspecter.firstElementChild as HTMLImageElement;
if(!image) {
let div = mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = div.firstElementChild as HTMLImageElement;
if(!image || image.tagName != 'IMG') {
image = new Image();
aspecter.append(image);
}
renderImageFromUrl(image, url);
//this.log('will renderImageFromUrl:', image, div, target);
renderImageFromUrl(image, url).then(() => {
div.append(image);
});
}
this.preloader.detach();

234
src/lib/appManagers/appMessagesManager.ts

@ -30,7 +30,7 @@ export type HistoryStorage = { @@ -30,7 +30,7 @@ export type HistoryStorage = {
history: number[],
pending: number[],
readPromise?: any,
readPromise?: Promise<boolean>,
maxOutID?: number,
reply_markup?: any
};
@ -1425,18 +1425,19 @@ export class AppMessagesManager { @@ -1425,18 +1425,19 @@ export class AppMessagesManager {
}
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);
const channelID = appPeersManager.isChannel(dialog.peerID) ? -dialog.peerID : 0;
const mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
const message = this.getMessage(mid);
let topDate = message.date;
if(channelID) {
let channel = appChatsManager.getChat(channelID);
const channel = appChatsManager.getChat(channelID);
if(!topDate || channel.date && channel.date > topDate) {
topDate = channel.date;
}
}
let savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
const savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
if(savedDraft && savedDraft.date > topDate) {
topDate = savedDraft.date;
}
@ -1619,20 +1620,20 @@ export class AppMessagesManager { @@ -1619,20 +1620,20 @@ export class AppMessagesManager {
return;
}
var peerID = this.getMessagePeer(apiMessage);
var isChannel = apiMessage.to_id._ == 'peerChannel';
var channelID = isChannel ? -peerID : 0;
var isBroadcast = isChannel && appChatsManager.isBroadcast(channelID);
const peerID = this.getMessagePeer(apiMessage);
const isChannel = apiMessage.to_id._ == 'peerChannel';
const channelID = isChannel ? -peerID : 0;
const isBroadcast = isChannel && appChatsManager.isBroadcast(channelID);
var mid = appMessagesIDsManager.getFullMessageID(apiMessage.id, channelID);
const mid = appMessagesIDsManager.getFullMessageID(apiMessage.id, channelID);
apiMessage.mid = mid;
if(apiMessage.grouped_id) {
let storage = this.groupedMessagesStorage[apiMessage.grouped_id] ?? (this.groupedMessagesStorage[apiMessage.grouped_id] = {});
const storage = this.groupedMessagesStorage[apiMessage.grouped_id] ?? (this.groupedMessagesStorage[apiMessage.grouped_id] = {});
storage[mid] = apiMessage;
}
var dialog = this.getDialogByPeerID(peerID)[0];
const dialog = this.getDialogByPeerID(peerID)[0];
if(dialog && mid > 0) {
apiMessage.pFlags.unread = mid > dialog[apiMessage.pFlags.out
? 'read_outbox_max_id'
@ -1651,12 +1652,12 @@ export class AppMessagesManager { @@ -1651,12 +1652,12 @@ export class AppMessagesManager {
apiMessage.peerID = peerID;
apiMessage.fromID = apiMessage.pFlags.post ? peerID : apiMessage.from_id;
var fwdHeader = apiMessage.fwd_from;
const fwdHeader = apiMessage.fwd_from;
if(fwdHeader) {
if(peerID == appUsersManager.getSelf().id) {
if(fwdHeader.saved_from_peer && fwdHeader.saved_from_msg_id) {
var savedFromPeerID = appPeersManager.getPeerID(fwdHeader.saved_from_peer);
var savedFromMid = appMessagesIDsManager.getFullMessageID(fwdHeader.saved_from_msg_id,
const savedFromPeerID = appPeersManager.getPeerID(fwdHeader.saved_from_peer);
const savedFromMid = appMessagesIDsManager.getFullMessageID(fwdHeader.saved_from_msg_id,
appPeersManager.isChannel(savedFromPeerID) ? -savedFromPeerID : 0);
apiMessage.savedFrom = savedFromPeerID + '_' + savedFromMid;
}
@ -1675,7 +1676,7 @@ export class AppMessagesManager { @@ -1675,7 +1676,7 @@ export class AppMessagesManager {
apiMessage.viaBotID = apiMessage.via_bot_id;
}
var mediaContext = {
const mediaContext = {
user_id: apiMessage.fromID,
date: apiMessage.date
};
@ -1725,8 +1726,8 @@ export class AppMessagesManager { @@ -1725,8 +1726,8 @@ export class AppMessagesManager {
}
if(apiMessage.action) {
var migrateFrom;
var migrateTo;
let migrateFrom;
let migrateTo;
switch(apiMessage.action._) {
case 'messageActionChatEditPhoto':
apiMessage.action.photo = appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
@ -1810,8 +1811,8 @@ export class AppMessagesManager { @@ -1810,8 +1811,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 || [];
const myEntities = RichTextProcessor.parseEntities(apiMessage.message);
const apiEntities = apiMessage.entities || [];
apiMessage.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !apiMessage.pending);
}
@ -2037,22 +2038,24 @@ export class AppMessagesManager { @@ -2037,22 +2038,24 @@ export class AppMessagesManager {
}
public canEditMessage(messageID: number) {
if (!this.messagesStorage[messageID]) {
return false
if(!this.messagesStorage[messageID]) {
return false;
}
var message = this.messagesStorage[messageID]
if (!message ||
!message.canBeEdited) {
return false
const message = this.messagesStorage[messageID];
if(!message || !message.canBeEdited) {
return false;
}
if (this.getMessagePeer(message) == appUsersManager.getSelf().id) {
return true
if(this.getMessagePeer(message) == appUsersManager.getSelf().id) {
return true;
}
if (message.date < tsNow(true) - 2 * 86400 ||
!message.pFlags.out) {
return false
if(message.date < tsNow(true) - 2 * 86400 || !message.pFlags.out) {
return false;
}
return true
return true;
}
public applyConversations(dialogsResult: any) {
@ -2062,12 +2065,12 @@ export class AppMessagesManager { @@ -2062,12 +2065,12 @@ export class AppMessagesManager {
//console.log('applyConversation', dialogsResult);
var updatedDialogs: {[peerID: number]: Dialog} = {};
var hasUpdated = false;
const updatedDialogs: {[peerID: number]: Dialog} = {};
let hasUpdated = false;
dialogsResult.dialogs.forEach((dialog: any) => {
var peerID = appPeersManager.getPeerID(dialog.peer);
var topMessage = dialog.top_message;
var topPendingMesage = this.pendingTopMsgs[peerID];
const peerID = appPeersManager.getPeerID(dialog.peer);
let topMessage = dialog.top_message;
const topPendingMesage = this.pendingTopMsgs[peerID];
if(topPendingMesage) {
if(!topMessage || this.getMessage(topPendingMesage).date > this.getMessage(topMessage).date) {
dialog.top_message = topMessage = topPendingMesage;
@ -2075,7 +2078,7 @@ export class AppMessagesManager { @@ -2075,7 +2078,7 @@ export class AppMessagesManager {
}
if(topMessage) {
let wasDialogBefore = this.getDialogByPeerID(peerID)[0];
const wasDialogBefore = this.getDialogByPeerID(peerID)[0];
// here need to just replace, not FULL replace dialog! WARNING
if(wasDialogBefore && wasDialogBefore.pFlags && wasDialogBefore.pFlags.pinned) {
@ -2093,7 +2096,7 @@ export class AppMessagesManager { @@ -2093,7 +2096,7 @@ export class AppMessagesManager {
hasUpdated = true;
}
} else {
var foundDialog = this.getDialogByPeerID(peerID);
const foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog.length) {
this.dialogsStorage[foundDialog[0].folder_id].splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: peerID, dialog: foundDialog[0]});
@ -2101,8 +2104,8 @@ export class AppMessagesManager { @@ -2101,8 +2104,8 @@ export class AppMessagesManager {
}
if(this.newUpdatesAfterReloadToHandle[peerID] !== undefined) {
for(let i in this.newUpdatesAfterReloadToHandle[peerID]) {
let update = this.newUpdatesAfterReloadToHandle[peerID][i];
for(const i in this.newUpdatesAfterReloadToHandle[peerID]) {
const update = this.newUpdatesAfterReloadToHandle[peerID][i];
this.handleUpdate(update);
}
@ -2534,27 +2537,21 @@ export class AppMessagesManager { @@ -2534,27 +2537,21 @@ export class AppMessagesManager {
}
}
public readHistory(peerID: number, maxID = 0, minID = 0): Promise<boolean> {
public readHistory(peerID: number, maxID = 0, readLength = 0): Promise<boolean> {
// console.trace('start read')
var isChannel = appPeersManager.isChannel(peerID);
var historyStorage = this.historiesStorage[peerID];
var foundDialog = this.getDialogByPeerID(peerID)[0];
const isChannel = appPeersManager.isChannel(peerID);
const historyStorage = this.historiesStorage[peerID];
const foundDialog = this.getDialogByPeerID(peerID)[0];
if(!foundDialog || !foundDialog.unread_count) {
if(!historyStorage || !historyStorage.history.length) {
return Promise.resolve(false);
}
let messageID, message;
let foundUnread = false;
for(let i = historyStorage.history.length; i >= 0; i--) {
messageID = historyStorage.history[i];
message = this.messagesStorage[messageID];
if(message && !message.pFlags.out && message.pFlags.unread) {
foundUnread = true;
break;
}
}
let foundUnread = !!historyStorage.history.find(messageID => {
const message = this.messagesStorage[messageID];
return message && !message.pFlags.out && message.pFlags.unread;
});
if(!foundUnread) {
return Promise.resolve(false);
@ -2562,10 +2559,10 @@ export class AppMessagesManager { @@ -2562,10 +2559,10 @@ export class AppMessagesManager {
}
if(historyStorage.readPromise) {
return historyStorage.readPromise as Promise<boolean>;
return historyStorage.readPromise;
}
var apiPromise: any;
let apiPromise: any;
if(isChannel) {
apiPromise = apiManager.invokeApi('channels.readHistory', {
channel: appChatsManager.getChannelInput(-peerID),
@ -2588,22 +2585,46 @@ export class AppMessagesManager { @@ -2588,22 +2585,46 @@ export class AppMessagesManager {
}
historyStorage.readPromise = apiPromise.then(() => {
let index = -1;
if(maxID != 0 && historyStorage.history.length) {
index = historyStorage.history.indexOf(maxID);
}
let readedLength = 0;
if(historyStorage.history.length && maxID) {
for(let i = index == -1 ? 0 : index, length = historyStorage.history.length; i < length; i++) {
const messageID = historyStorage.history[i];
if(messageID > maxID) continue;
const message = this.messagesStorage[messageID];
if(message && !message.pFlags.out) {
message.pFlags.unread = false;
readedLength++;
//NotificationsManager.cancel('msg' + messageID); // warning
}
}
}
if(foundDialog) {
// console.log('done read history', peerID)
let index = -1;
if(maxID != 0 && historyStorage && historyStorage.history.length) {
index = historyStorage.history.findIndex((mid: number) => mid == maxID);
if(historyStorage.history.length) {
////////console.warn('readPromise:', index, historyStorage.history[index != -1 ? index : 0]);
foundDialog.read_inbox_max_id = maxID;
}
foundDialog.unread_count = index == -1 ? 0 : index;
////////console.log('readHistory set unread_count to:', foundDialog.unread_count, foundDialog);
if(foundDialog.read_inbox_max_id == foundDialog.top_message || foundDialog.read_inbox_max_id == foundDialog.read_outbox_max_id) {
foundDialog.unread_count = 0;
} else {
foundDialog.unread_count = Math.max(foundDialog.unread_count - (readLength || readedLength), 0);
}
console.log('readHistory set unread_count to:', foundDialog.unread_count, foundDialog);
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: foundDialog.unread_count});
$rootScope.$broadcast('messages_read');
if(historyStorage && historyStorage.history.length) {
////////console.warn('readPromise:', index, historyStorage.history[index != -1 ? index : 0]);
foundDialog.read_inbox_max_id = historyStorage.history[index != -1 ? index : 0];
}
return true;
}
@ -2613,23 +2634,6 @@ export class AppMessagesManager { @@ -2613,23 +2634,6 @@ export class AppMessagesManager {
delete historyStorage.readPromise;
});
if(historyStorage && historyStorage.history.length) {
let messageID: number;
let message, i;
for(i = 0; i < historyStorage.history.length; i++) {
messageID = historyStorage.history[i];
message = this.messagesStorage[messageID];
if(message && !message.pFlags.out) {
message.pFlags.unread = false;
//NotificationsManager.cancel('msg' + messageID); // warning
}
if(messageID == minID) break;
}
}
// NotificationsManager.soundReset(appPeersManager.getPeerString(peerID)) // warning
return historyStorage.readPromise;
@ -3346,14 +3350,16 @@ export class AppMessagesManager { @@ -3346,14 +3350,16 @@ export class AppMessagesManager {
if(this.migratedFromTo[peerID]) {
peerID = this.migratedFromTo[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;
var isMigrated = false;
var reqPeerID = peerID;
const historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {count: null, history: [], pending: []});
const unreadOffset = 0;
const unreadSkip = false;
let offset = 0;
let offsetNotFound = false;
let isMigrated = false;
let reqPeerID = peerID;
if(this.migratedToFrom[peerID]) {
isMigrated = true;
if(maxID && maxID < appMessagesIDsManager.fullMsgIDModulus) {
@ -3363,7 +3369,7 @@ export class AppMessagesManager { @@ -3363,7 +3369,7 @@ export class AppMessagesManager {
if(maxID > 0) {
offsetNotFound = true;
for(offset = 0; offset < historyStorage.history.length; offset++) {
for(; offset < historyStorage.history.length; offset++) {
if(maxID > historyStorage.history[offset]) {
offsetNotFound = false;
break;
@ -3383,7 +3389,7 @@ export class AppMessagesManager { @@ -3383,7 +3389,7 @@ export class AppMessagesManager {
limit = limit;
}
var history = historyStorage.history.slice(offset, offset + limit);
let history = historyStorage.history.slice(offset, offset + limit);
if(!maxID && historyStorage.pending.length) {
history = historyStorage.pending.slice().concat(history);
}
@ -3411,10 +3417,10 @@ export class AppMessagesManager { @@ -3411,10 +3417,10 @@ export class AppMessagesManager {
historyStorage.count++;
}
var history: number[] = [];
let history: number[] = [];
historyResult.messages.forEach((message: any) => {
history.push(message.mid);
})
});
if(!maxID && historyStorage.pending.length) {
history = historyStorage.pending.slice().concat(history);
@ -3455,25 +3461,26 @@ export class AppMessagesManager { @@ -3455,25 +3461,26 @@ export class AppMessagesManager {
public fillHistoryStorage(peerID: number, maxID: number, fullLimit: number, historyStorage: HistoryStorage): Promise<boolean> {
// console.log('fill history storage', peerID, maxID, fullLimit, angular.copy(historyStorage))
var offset = (this.migratedFromTo[peerID] && !maxID) ? 1 : 0;
const offset = (this.migratedFromTo[peerID] && !maxID) ? 1 : 0;
return this.requestHistory(peerID, maxID, fullLimit, offset).then((historyResult: any) => {
historyStorage.count = historyResult.count || historyResult.messages.length;
var offset = 0;
if(!maxID && historyResult.messages.length) {
maxID = historyResult.messages[0].mid + 1;
}
let offset = 0;
if(maxID > 0) {
for(offset = 0; offset < historyStorage.history.length; offset++) {
for(; offset < historyStorage.history.length; offset++) {
if(maxID > historyStorage.history[offset]) {
break;
}
}
}
var wasTotalCount = historyStorage.history.length;
const wasTotalCount = historyStorage.history.length;
historyStorage.history.splice(offset, historyStorage.history.length - offset)
historyStorage.history.splice(offset, historyStorage.history.length - offset);
historyResult.messages.forEach((message: any) => {
if(this.mergeReplyKeyboard(historyStorage, message)) {
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
@ -3482,12 +3489,12 @@ export class AppMessagesManager { @@ -3482,12 +3489,12 @@ export class AppMessagesManager {
historyStorage.history.push(message.mid);
});
var totalCount = historyStorage.history.length;
const totalCount = historyStorage.history.length;
fullLimit -= (totalCount - wasTotalCount);
var migratedNextPeer = this.migratedFromTo[peerID];
var migratedPrevPeer = this.migratedToFrom[peerID]
var isMigrated = migratedNextPeer !== undefined || migratedPrevPeer !== undefined;
const migratedNextPeer = this.migratedFromTo[peerID];
const migratedPrevPeer = this.migratedToFrom[peerID]
const isMigrated = migratedNextPeer !== undefined || migratedPrevPeer !== undefined;
if(isMigrated) {
historyStorage.count = Math.max(historyStorage.count, totalCount) + 1;
@ -3517,12 +3524,9 @@ export class AppMessagesManager { @@ -3517,12 +3524,9 @@ export class AppMessagesManager {
}
public wrapHistoryResult(result: HistoryResult) {
var unreadOffset = result.unreadOffset;
if(unreadOffset) {
var i;
var message;
for(i = result.history.length - 1; i >= 0; i--) {
message = this.messagesStorage[result.history[i]];
if(result.unreadOffset) {
for(let i = result.history.length - 1; i >= 0; i--) {
const message = this.messagesStorage[result.history[i]];
if(message && !message.pFlags.out && message.pFlags.unread) {
result.unreadOffset = i + 1;
break;
@ -3533,7 +3537,7 @@ export class AppMessagesManager { @@ -3533,7 +3537,7 @@ export class AppMessagesManager {
}
public requestHistory(peerID: number, maxID: number, limit: number, offset = 0): Promise<any> {
var isChannel = appPeersManager.isChannel(peerID);
const isChannel = appPeersManager.isChannel(peerID);
//console.trace('requestHistory', peerID, maxID, limit, offset);
@ -3562,7 +3566,7 @@ export class AppMessagesManager { @@ -3562,7 +3566,7 @@ export class AppMessagesManager {
apiUpdatesManager.addChannelState(-peerID, historyResult.pts);
}
var length = historyResult.messages.length;
let length = historyResult.messages.length;
if(length && historyResult.messages[length - 1].deleted) {
historyResult.messages.splice(length - 1, 1);
length--;
@ -3570,7 +3574,7 @@ export class AppMessagesManager { @@ -3570,7 +3574,7 @@ export class AppMessagesManager {
}
// will load more history if last message is album grouped (because it can be not last item)
let historyStorage = this.historiesStorage[peerID];
const historyStorage = this.historiesStorage[peerID];
// historyResult.messages: desc sorted
if(length && historyResult.messages[length - 1].grouped_id && (historyStorage.history.length + historyResult.messages.length) < historyResult.count) {
return this.requestHistory(peerID, historyResult.messages[length - 1].mid, 10, 0).then((_historyResult: any) => {
@ -3614,7 +3618,7 @@ export class AppMessagesManager { @@ -3614,7 +3618,7 @@ export class AppMessagesManager {
}, (error) => {
switch (error.type) {
case 'CHANNEL_PRIVATE':
var channel = appChatsManager.getChat(-peerID);
let channel = appChatsManager.getChat(-peerID);
channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title};
apiUpdatesManager.processUpdateMessage({
_: 'updates',
@ -3628,7 +3632,7 @@ export class AppMessagesManager { @@ -3628,7 +3632,7 @@ export class AppMessagesManager {
break;
}
return Promise.reject(error);
throw error;
});
}

7
src/lib/appManagers/appSidebarRight.ts

@ -219,12 +219,7 @@ class AppSidebarRight { @@ -219,12 +219,7 @@ class AppSidebarRight {
if(!willChange) return Promise.resolve();
let set = () => {
if(enable !== undefined) {
if(enable) this.sidebarEl.classList.add('active');
else this.sidebarEl.classList.remove('active');
} else {
this.sidebarEl.classList.toggle('active');
}
this.sidebarEl.classList.toggle('active', enable);
};
return new Promise((resolve, reject) => {

2
src/lib/appManagers/appStickersManager.ts

@ -138,7 +138,7 @@ class AppStickersManager { @@ -138,7 +138,7 @@ class AppStickersManager {
public getAnimatedEmojiSticker(emoji: string) {
let stickerSet = this.stickerSets.emoji;
emoji = emoji.replace(/\ufe0f/g, '');
emoji = emoji.replace(/\ufe0f/g, '').replace(/🏻|🏼|🏽|🏾|🏿/g, '');
return stickerSet.documents.find(doc => doc.stickerEmojiRaw == emoji);
}

2
src/lib/config.ts

File diff suppressed because one or more lines are too long

76
src/lib/lottieLoader.ts

@ -1,6 +1,10 @@ @@ -1,6 +1,10 @@
import { isInDOM } from "./utils";
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d";
let convert = (value: number) => {
return Math.round(Math.min(Math.max(value, 0), 1) * 255);
};
class LottieLoader {
public lottie: /* any */ typeof LottiePlayer = null;
private animations: {
@ -16,6 +20,35 @@ class LottieLoader { @@ -16,6 +20,35 @@ class LottieLoader {
public loaded: Promise<void>;
private lastTimeLoad = 0;
private waitingTimeouts = 0;
private static COLORREPLACEMENTS = [
[
[0xf77e41, 0xca907a],
[0xffb139, 0xedc5a5],
[0xffd140, 0xf7e3c3],
[0xffdf79, 0xfbefd6],
],
[
[0xf77e41, 0xaa7c60],
[0xffb139, 0xc8a987],
[0xffd140, 0xddc89f],
[0xffdf79, 0xe6d6b2],
],
[
[0xf77e41, 0x8c6148],
[0xffb139, 0xad8562],
[0xffd140, 0xc49e76],
[0xffdf79, 0xd4b188],
],
[
[0xf77e41, 0x6e3c2c],
[0xffb139, 0x925a34],
[0xffd140, 0xa16e46],
[0xffdf79, 0xac7a52],
]
];
public loadLottie() {
if(this.loaded) return this.loaded;
@ -89,12 +122,53 @@ class LottieLoader { @@ -89,12 +122,53 @@ class LottieLoader {
}
}
public async loadAnimation(params: /* any */AnimationConfigWithPath | AnimationConfigWithData, group = '') {
private applyReplacements(object: any, toneIndex: number) {
const replacements = LottieLoader.COLORREPLACEMENTS[toneIndex - 2];
const iterateIt = (it: any) => {
for(let smth of it) {
switch(smth.ty) {
case 'st':
case 'fl':
let k = smth.c.k;
let color = convert(k[2]) | (convert(k[1]) << 8) | (convert(k[0]) << 16);
let foundReplacement = replacements.find(p => p[0] == color);
if(foundReplacement) {
k[0] = ((foundReplacement[1] >> 16) & 255) / 255;
k[1] = ((foundReplacement[1] >> 8) & 255) / 255;
k[2] = (foundReplacement[1] & 255) / 255;
}
console.log('foundReplacement!', foundReplacement, color.toString(16), k);
break;
}
if(smth.hasOwnProperty('it')) {
iterateIt(smth.it);
}
}
};
for(let layer of object.layers) {
if(!layer.shapes) continue;
for(let shape of layer.shapes) {
iterateIt(shape.it);
}
}
}
public async loadAnimation(params: /* any */AnimationConfigWithPath & AnimationConfigWithData, group = '', toneIndex = -1) {
//params.autoplay = false;
//if(group != 'auth') {
//params.renderer = 'canvas';
params.renderer = 'svg';
//}
if(toneIndex >= 1 && toneIndex <= 5) {
this.applyReplacements(params.animationData, toneIndex);
}
let rendererSettings = {
//context: context, // the canvas context

27
src/lib/utils.js

@ -634,17 +634,17 @@ emojiUnicode.raw = function(input) { @@ -634,17 +634,17 @@ emojiUnicode.raw = function(input) {
for(var i = 0; i < input.length; i++) {
// high surrogate
if(input.charCodeAt(i) >= 0xd800 && input.charCodeAt(i) <= 0xdbff) {
if(input.charCodeAt(i + 1) >= 0xdc00 && input.charCodeAt(i + 1) <= 0xdfff) {
// low surrogate
pairs.push(
(input.charCodeAt(i) - 0xd800) * 0x400
+ (input.charCodeAt(i + 1) - 0xdc00) + 0x10000
);
}
} else if (input.charCodeAt(i) < 0xd800 || input.charCodeAt(i) > 0xdfff) {
// modifiers and joiners
pairs.push(input.charCodeAt(i))
}
if(input.charCodeAt(i + 1) >= 0xdc00 && input.charCodeAt(i + 1) <= 0xdfff) {
// low surrogate
pairs.push(
(input.charCodeAt(i) - 0xd800) * 0x400
+ (input.charCodeAt(i + 1) - 0xdc00) + 0x10000
);
}
} else if(input.charCodeAt(i) < 0xd800 || input.charCodeAt(i) > 0xdfff) {
// modifiers and joiners
pairs.push(input.charCodeAt(i))
}
}
return pairs.join(' ');
@ -653,6 +653,11 @@ emojiUnicode.raw = function(input) { @@ -653,6 +653,11 @@ emojiUnicode.raw = function(input) {
return '';
};
export function getEmojiToneIndex(input) {
let match = input.match(/[\uDFFB-\uDFFF]/);
return match ? 5 - (57343 - match[0].charCodeAt(0)) : 0;
}
//var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g,
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g,
trimRe = /^\s+|\s$/g

45
src/scss/partials/_chatBubble.scss

@ -12,6 +12,31 @@ @@ -12,6 +12,31 @@
}
}
.bubbles-date-group {
position: relative;
/* .sticky_sentinel {
visibility: visible;
background: #000;
} */
.sticky_sentinel--top {
/* Adjust the height and top values based on your on your sticky top position.
e.g. make the height bigger and adjust the top so observeHeaders()'s
IntersectionObserver fires as soon as the bottom of the sentinel crosses the
top of the intersection container. */
height: 5px;
top: 0;
}
// .sticky_sentinel--bottom {
// /* Height should match the top of the header when it's at the bottom of the
// intersection container. */
// height: 1px;
// bottom: 0;
// }
}
.bubble {
padding-top: 5px;
max-width: $chat-max-width;
@ -249,8 +274,8 @@ @@ -249,8 +274,8 @@
}
&.sticker .bubble__container {
max-width: 140px;
max-height: 140px;
max-width: 140px !important;
max-height: 140px !important;
}
}
@ -319,15 +344,15 @@ @@ -319,15 +344,15 @@
}
.bubble__container {
max-width: 200px;
max-height: 200px;
max-width: 200px !important;
max-height: 200px !important;
}
}
&.round {
.attachment {
max-width: 200px;
max-height: 200px;
max-width: 200px !important;
max-height: 200px !important;
img {
border-radius: 50%;
@ -378,7 +403,9 @@ @@ -378,7 +403,9 @@
color: #fff;
text-align: center;
}
}
.download, .preloader-container {
& ~ .video-play {
display: none;
}
@ -930,6 +957,7 @@ @@ -930,6 +957,7 @@
display: flex;
align-items: center;
justify-content: center;
text-align: center;
.name {
cursor: pointer;
@ -1313,7 +1341,6 @@ poll-element { @@ -1313,7 +1341,6 @@ poll-element {
&-answer {
display: flex;
flex-direction: row;
position: relative;
padding-bottom: 20px;
padding-left: 34px;
@ -1339,7 +1366,7 @@ poll-element { @@ -1339,7 +1366,7 @@ poll-element {
&-selected {
position: absolute;
top: 33px;
bottom: 3px;
left: 26px;
color: #fff;
background: #50a2e9;
@ -1379,7 +1406,7 @@ poll-element { @@ -1379,7 +1406,7 @@ poll-element {
height: 35px;
position: absolute;
left: 17.5px;
top: 11px;
bottom: 2px;
transition: stroke-dashoffset .34s linear, stroke-dasharray .34s linear;
stroke-dashoffset: 0;
stroke-dasharray: 0, 485.9;

8
src/scss/style.scss

@ -1376,6 +1376,14 @@ img.emoji { @@ -1376,6 +1376,14 @@ img.emoji {
transform: translate(-50%, -50%);
}
.sticky_sentinel {
position: absolute;
left: 0;
right: 0; /* needs dimensions */
visibility: hidden;
pointer-events: none;
}
.page-chats {
/* display: grid; */
/* grid-template-columns: 25% 50%; */

Loading…
Cancel
Save