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. 5
      src/components/scrollable_new.ts
  6. 76
      src/components/stickyIntersector.ts
  7. 39
      src/components/wrappers.ts
  8. 2
      src/emoji.json
  9. 20
      src/format_jsons.js
  10. 2
      src/lib/appManagers/appDialogsManager.ts
  11. 186
      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. 7
      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 {
} }
}); });
let emoticonsDisplayTimeout = 0;
this.toggleEmoticons.onmouseover = (e) => { this.toggleEmoticons.onmouseover = (e) => {
clearTimeout(this.emoticonsTimeout); clearTimeout(this.emoticonsTimeout);
this.emoticonsTimeout = setTimeout(() => { this.emoticonsTimeout = setTimeout(() => {
@ -464,6 +465,11 @@ export class ChatInput {
this.toggleEmoticons.classList.remove('active'); this.toggleEmoticons.classList.remove('active');
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP); lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
this.emoticonsLazyLoadQueue.lock(); this.emoticonsLazyLoadQueue.lock();
clearTimeout(emoticonsDisplayTimeout);
emoticonsDisplayTimeout = setTimeout(() => {
this.emoticonsDropdown.style.display = 'none';
}, 200);
}, 200); }, 200);
}; };
@ -471,8 +477,11 @@ export class ChatInput {
clearTimeout(this.emoticonsTimeout); clearTimeout(this.emoticonsTimeout);
}; };
} else { } else {
this.emoticonsDropdown.style.display = '';
void this.emoticonsDropdown.offsetLeft; // reflow
this.emoticonsDropdown.classList.add('active'); this.emoticonsDropdown.classList.add('active');
this.emoticonsLazyLoadQueue.unlock(); this.emoticonsLazyLoadQueue.unlock();
clearTimeout(emoticonsDisplayTimeout);
} }
this.toggleEmoticons.classList.add('active'); this.toggleEmoticons.classList.add('active');

14
src/components/emoticonsDropdown.ts

@ -290,7 +290,13 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
docs.forEach(doc => { docs.forEach(doc => {
let div = document.createElement('div'); 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); itemsDiv.append(div);
}); });
@ -408,7 +414,11 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
} }
}); });
} else { // as thumb will be used first sticker } 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); categoryPush(categoryDiv, stickerSet.set.title, stickerSet.documents, false);

2
src/components/poll.ts

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

5
src/components/preloader.ts

@ -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) { if(promise) {
this.promise = promise; this.promise = promise;
@ -75,7 +75,8 @@ export default class ProgressivePreloader {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
if(this.detached) return; if(this.detached) return;
this.detached = false; this.detached = false;
elem.append(this.preloader);
elem[append ? 'append' : 'prepend'](this.preloader);
}); });
/* let isIn = isInDOM(this.preloader); /* let isIn = isInDOM(this.preloader);

5
src/components/scrollable_new.ts

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

76
src/components/stickyIntersector.ts

@ -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);
}
}

39
src/components/wrappers.ts

@ -4,7 +4,7 @@ import apiManager from '../lib/mtproto/mtprotoworker';
import LottieLoader from '../lib/lottieLoader'; import LottieLoader from '../lib/lottieLoader';
import appStickersManager from "../lib/appManagers/appStickersManager"; import appStickersManager from "../lib/appManagers/appStickersManager";
import appDocsManager from "../lib/appManagers/appDocsManager"; import appDocsManager from "../lib/appManagers/appDocsManager";
import { formatBytes } from "../lib/utils"; import { formatBytes, getEmojiToneIndex } from "../lib/utils";
import ProgressivePreloader from './preloader'; import ProgressivePreloader from './preloader';
import LazyLoadQueue from './lazyLoadQueue'; import LazyLoadQueue from './lazyLoadQueue';
import apiFileManager from '../lib/mtproto/apiFileManager'; import apiFileManager from '../lib/mtproto/apiFileManager';
@ -108,7 +108,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
container.append(video); container.append(video);
} }
let span: HTMLSpanElement; let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
if(doc.type != 'round') { if(doc.type != 'round') {
span = document.createElement('span'); span = document.createElement('span');
span.classList.add('video-time'); span.classList.add('video-time');
@ -117,7 +117,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
if(doc.type != 'gif') { if(doc.type != 'gif') {
span.innerText = (doc.duration + '').toHHMMSS(false); 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'); spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
container.append(spanPlay); container.append(spanPlay);
} else { } else {
@ -127,11 +127,11 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
let loadVideo = async() => { let loadVideo = async() => {
if(message.media.preloader) { // means upload 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) { } else if(!doc.downloaded) {
let preloader = new ProgressivePreloader(container, true); let preloader = new ProgressivePreloader(container, true);
let promise = appDocsManager.downloadDoc(doc); let promise = appDocsManager.downloadDoc(doc);
preloader.attach(container, true, promise); preloader.attach(container, true, promise, false);
await promise; await promise;
} }
@ -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}); 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; let stickerType = doc.sticker;
if(stickerType == 2 && !LottieLoader.loaded) { if(stickerType == 2 && !LottieLoader.loaded) {
@ -700,6 +709,8 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
//console.log('wrap sticker', doc, div, onlyThumb); //console.log('wrap sticker', doc, div, onlyThumb);
const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1;
if(doc.thumbs && !div.firstElementChild && (!doc.downloaded || stickerType == 2)) { if(doc.thumbs && !div.firstElementChild && (!doc.downloaded || stickerType == 2)) {
let thumb = doc.thumbs[0]; let thumb = doc.thumbs[0];
@ -777,29 +788,30 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
const reader = new FileReader(); const reader = new FileReader();
reader.addEventListener('loadend', async(e) => { reader.addEventListener('loadend', async(e) => {
console.time('decompress sticker' + doc.id); //console.time('decompress sticker' + doc.id);
console.time('render sticker' + doc.id); //console.time('render sticker' + doc.id);
// @ts-ignore // @ts-ignore
const text = e.srcElement.result; const text = e.srcElement.result;
let json = await apiManager.gzipUncompress<string>(text, true); 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({ let animation = await LottieLoader.loadAnimation({
container: div, container: div,
loop: false, loop: false,
autoplay: false, autoplay: false,
animationData: JSON.parse(json), animationData: JSON.parse(json),
renderer: canvas ? 'canvas' : 'svg' renderer: 'svg'
}, group); }, group, toneIndex);
console.timeEnd('render sticker' + doc.id); //console.timeEnd('render sticker' + doc.id);
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') { if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') {
div.firstElementChild.remove(); div.firstElementChild.remove();
} }
if(!canvas) {
div.addEventListener('mouseover', (e) => { div.addEventListener('mouseover', (e) => {
let animation = LottieLoader.getAnimation(div, group); let animation = LottieLoader.getAnimation(div, group);
@ -822,7 +834,6 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
}, {once: true}); }, {once: true});
} }
}); });
}
if(play) { if(play) {
animation.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) {
{ {
let categories = { let categories = {
"Smileys & Emotion": 1 "Smileys & Emotion": 1
, "People & Body": 1
, "Animals & Nature": 2 , "Animals & Nature": 2
, "Food & Drink": 3 , "Food & Drink": 3
, "Travel & Places": 4 , "Travel & Places": 4
@ -58,18 +59,23 @@ if(false) {
, "Skin Tones": 8 , "Skin Tones": 8
}; };
let concatCategories = [['Objects', 'Symbols'], ['Smileys & Emotion', 'People & Body']];
let maxIndexes = {};
let maxObjectsIndex = -1; let maxObjectsIndex = -1;
formatted.forEach(e => { formatted.forEach(e => {
if(e.category == 'Objects') { if(concatCategories.findIndex(c => c[0] == e.category) === -1) return;
if(e.sort_order > maxObjectsIndex) {
maxObjectsIndex = e.sort_order; 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 => { formatted.forEach(e => {
if(e.category == 'Symbols') { let concatDetails = concatCategories.find(c => c[1] == e.category);
e.sort_order += maxObjectsIndex; if(!concatDetails) return;
}
e.sort_order += maxIndexes[concatDetails[0]];
}); });
formatted.forEach(e => { formatted.forEach(e => {

2
src/lib/appManagers/appDialogsManager.ts

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

186
src/lib/appManagers/appImManager.ts

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

64
src/lib/appManagers/appMediaViewer.ts

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

234
src/lib/appManagers/appMessagesManager.ts

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

7
src/lib/appManagers/appSidebarRight.ts

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

2
src/lib/appManagers/appStickersManager.ts

@ -138,7 +138,7 @@ class AppStickersManager {
public getAnimatedEmojiSticker(emoji: string) { public getAnimatedEmojiSticker(emoji: string) {
let stickerSet = this.stickerSets.emoji; 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); 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 @@
import { isInDOM } from "./utils"; import { isInDOM } from "./utils";
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d"; 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 { class LottieLoader {
public lottie: /* any */ typeof LottiePlayer = null; public lottie: /* any */ typeof LottiePlayer = null;
private animations: { private animations: {
@ -16,6 +20,35 @@ class LottieLoader {
public loaded: Promise<void>; public loaded: Promise<void>;
private lastTimeLoad = 0; private lastTimeLoad = 0;
private waitingTimeouts = 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() { public loadLottie() {
if(this.loaded) return this.loaded; if(this.loaded) return this.loaded;
@ -89,13 +122,54 @@ 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; //params.autoplay = false;
//if(group != 'auth') { //if(group != 'auth') {
//params.renderer = 'canvas'; //params.renderer = 'canvas';
params.renderer = 'svg'; params.renderer = 'svg';
//} //}
if(toneIndex >= 1 && toneIndex <= 5) {
this.applyReplacements(params.animationData, toneIndex);
}
let rendererSettings = { let rendererSettings = {
//context: context, // the canvas context //context: context, // the canvas context
//preserveAspectRatio: 'xMinYMin slice', // Supports the same options as the svg element's preserveAspectRatio property //preserveAspectRatio: 'xMinYMin slice', // Supports the same options as the svg element's preserveAspectRatio property

7
src/lib/utils.js

@ -641,7 +641,7 @@ emojiUnicode.raw = function(input) {
+ (input.charCodeAt(i + 1) - 0xdc00) + 0x10000 + (input.charCodeAt(i + 1) - 0xdc00) + 0x10000
); );
} }
} else if (input.charCodeAt(i) < 0xd800 || input.charCodeAt(i) > 0xdfff) { } else if(input.charCodeAt(i) < 0xd800 || input.charCodeAt(i) > 0xdfff) {
// modifiers and joiners // modifiers and joiners
pairs.push(input.charCodeAt(i)) pairs.push(input.charCodeAt(i))
} }
@ -653,6 +653,11 @@ emojiUnicode.raw = function(input) {
return ''; 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 = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g,
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g, var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g,
trimRe = /^\s+|\s$/g trimRe = /^\s+|\s$/g

45
src/scss/partials/_chatBubble.scss

@ -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 { .bubble {
padding-top: 5px; padding-top: 5px;
max-width: $chat-max-width; max-width: $chat-max-width;
@ -249,8 +274,8 @@
} }
&.sticker .bubble__container { &.sticker .bubble__container {
max-width: 140px; max-width: 140px !important;
max-height: 140px; max-height: 140px !important;
} }
} }
@ -319,15 +344,15 @@
} }
.bubble__container { .bubble__container {
max-width: 200px; max-width: 200px !important;
max-height: 200px; max-height: 200px !important;
} }
} }
&.round { &.round {
.attachment { .attachment {
max-width: 200px; max-width: 200px !important;
max-height: 200px; max-height: 200px !important;
img { img {
border-radius: 50%; border-radius: 50%;
@ -378,7 +403,9 @@
color: #fff; color: #fff;
text-align: center; text-align: center;
} }
}
.download, .preloader-container {
& ~ .video-play { & ~ .video-play {
display: none; display: none;
} }
@ -930,6 +957,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center;
.name { .name {
cursor: pointer; cursor: pointer;
@ -1313,7 +1341,6 @@ poll-element {
&-answer { &-answer {
display: flex; display: flex;
flex-direction: row;
position: relative; position: relative;
padding-bottom: 20px; padding-bottom: 20px;
padding-left: 34px; padding-left: 34px;
@ -1339,7 +1366,7 @@ poll-element {
&-selected { &-selected {
position: absolute; position: absolute;
top: 33px; bottom: 3px;
left: 26px; left: 26px;
color: #fff; color: #fff;
background: #50a2e9; background: #50a2e9;
@ -1379,7 +1406,7 @@ poll-element {
height: 35px; height: 35px;
position: absolute; position: absolute;
left: 17.5px; left: 17.5px;
top: 11px; bottom: 2px;
transition: stroke-dashoffset .34s linear, stroke-dasharray .34s linear; transition: stroke-dashoffset .34s linear, stroke-dasharray .34s linear;
stroke-dashoffset: 0; stroke-dashoffset: 0;
stroke-dasharray: 0, 485.9; stroke-dasharray: 0, 485.9;

8
src/scss/style.scss

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

Loading…
Cancel
Save