Browse Source

chat switch fix & lazyload intersection & scroll intersection & sidebar open intersection & open chat speed improvements

master
morethanwords 4 years ago
parent
commit
52c563d6a3
  1. 1
      src/components/appForward.ts
  2. 10
      src/components/avatar.ts
  3. 11
      src/components/bubbleGroups.ts
  4. 9
      src/components/chatInput.ts
  5. 50
      src/components/emoticonsDropdown.ts
  6. 79
      src/components/lazyLoadQueue.ts
  7. 420
      src/components/scrollable copy.ts
  8. 139
      src/components/scrollable_new.ts
  9. 270
      src/components/scrollable_spliceCount.ts
  10. 11
      src/components/wrappers.ts
  11. 2
      src/countries.json
  12. 2
      src/emoji.json
  13. 59
      src/format_jsons.js
  14. 2
      src/lib/appManagers/apiUpdatesManager.ts
  15. 74
      src/lib/appManagers/appDialogsManager.ts
  16. 152
      src/lib/appManagers/appImManager.ts
  17. 9
      src/lib/appManagers/appMediaViewer.ts
  18. 58
      src/lib/appManagers/appMessagesManager.ts
  19. 41
      src/lib/appManagers/appSidebarLeft.ts
  20. 130
      src/lib/appManagers/appSidebarRight.ts
  21. 3
      src/lib/appManagers/appStickersManager.ts
  22. 42
      src/lib/config.ts
  23. 4
      src/lib/lottie.ts
  24. 15
      src/lib/lottieLoader.ts
  25. 9
      src/lib/mtproto/apiFileManager.ts
  26. 123
      src/lib/richtextprocessor.js
  27. 117
      src/lib/utils.js
  28. 26
      src/scss/partials/_chatBubble.scss
  29. 4
      src/scss/partials/_rightSIdebar.scss
  30. 8
      src/scss/partials/_scrollable.scss

1
src/components/appForward.ts

@ -15,7 +15,6 @@ class AppForward { @@ -15,7 +15,6 @@ class AppForward {
this.closeBtn.addEventListener('click', () => {
this.cleanup();
this.container.classList.remove('active');
appSidebarRight.onSidebarScroll();
});
this.sendBtn.addEventListener('click', () => {

10
src/components/avatar.ts

@ -14,6 +14,7 @@ $rootScope.$on('avatar_update', (e: CustomEvent) => { @@ -14,6 +14,7 @@ $rootScope.$on('avatar_update', (e: CustomEvent) => {
export default class AvatarElement extends HTMLElement {
private peerID: number;
private isDialog = false;
public peerTitle: string;
constructor() {
super();
@ -33,22 +34,23 @@ export default class AvatarElement extends HTMLElement { @@ -33,22 +34,23 @@ export default class AvatarElement extends HTMLElement {
}
static get observedAttributes(): string[] {
return ['peer', 'dialog'/* массив имён атрибутов для отслеживания их изменений */];
return ['peer', 'dialog', 'peer-title'/* массив имён атрибутов для отслеживания их изменений */];
}
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
// вызывается при изменении одного из перечисленных выше атрибутов
if(name == 'peer') {
this.peerID = +newValue;
this.update();
} else if(name == 'peer-title') {
this.peerTitle = newValue;
} else if(name == 'dialog') {
this.isDialog = !!newValue;
}
this.update();
}
public update() {
appProfileManager.putPhoto(this, this.peerID, this.isDialog);
appProfileManager.putPhoto(this, this.peerID, this.isDialog, this.peerTitle);
}
adoptedCallback() {

11
src/components/bubbleGroups.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { generatePathData } from "../lib/utils";
import { generatePathData, $rootScope } from "../lib/utils";
export default class BubbleGroups {
bubblesByGroups: Array<{timestamp: number, fromID: number, mid: number, group: HTMLDivElement[]}> = []; // map to group
@ -22,6 +22,11 @@ export default class BubbleGroups { @@ -22,6 +22,11 @@ export default class BubbleGroups {
let timestamp = message.date;
let fromID = message.fromID;
let group: HTMLDivElement[];
// fix for saved messages forward to self
if(fromID == $rootScope.myID && $rootScope.selectedPeerID == $rootScope.myID && message.fwdFromID == fromID) {
fromID = -fromID;
}
// try to find added
//this.removeBubble(message.mid);
@ -48,7 +53,7 @@ export default class BubbleGroups { @@ -48,7 +53,7 @@ export default class BubbleGroups {
this.groups.push(group = [bubble]);
}
//console.log('addBubble', bubble, message.mid, fromID, reverse, group);
//console.log('[BUBBLE]: addBubble', bubble, message.mid, fromID, reverse, group);
this.bubblesByGroups[reverse ? 'unshift' : 'push']({timestamp, fromID, mid: message.mid, group});
this.updateGroup(group);
@ -117,7 +122,7 @@ export default class BubbleGroups { @@ -117,7 +122,7 @@ export default class BubbleGroups {
let first = group[0];
//console.log('updateGroup', group, first);
//console.log('[BUBBLE]: updateGroup', group, first);
if(group.length == 1) {
first.classList.add('is-group-first', 'is-group-last');

9
src/components/chatInput.ts

@ -471,7 +471,6 @@ export class ChatInput { @@ -471,7 +471,6 @@ export class ChatInput {
};
} else {
this.emoticonsDropdown.classList.add('active');
this.emoticonsLazyLoadQueue.check();
}
this.toggleEmoticons.classList.add('active');
@ -513,11 +512,7 @@ export class ChatInput { @@ -513,11 +512,7 @@ export class ChatInput {
}, '');
};
public onMessageSent(scrollDown = true, clearInput = true) {
if(scrollDown) {
appImManager.scroll.scrollTop = appImManager.scroll.scrollHeight;
}
public onMessageSent(clearInput = true) {
let dialog = appMessagesManager.getDialogByPeerID(appImManager.peerID)[0];
if(dialog && dialog.top_message) {
appMessagesManager.readHistory(appImManager.peerID, dialog.top_message); // lol
@ -556,7 +551,7 @@ export class ChatInput { @@ -556,7 +551,7 @@ export class ChatInput {
});
}
this.onMessageSent(!this.editMsgID);
this.onMessageSent();
};
public setTopInfo(title: string, subtitle: string, input?: string, message?: any) {

50
src/components/emoticonsDropdown.ts

@ -39,7 +39,6 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -39,7 +39,6 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}
}, () => {
lottieLoader.checkAnimations(false, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check(); // for stickers or gifs
});
(tabs.firstElementChild.children[0] as HTMLLIElement).click(); // set emoji tab
@ -66,13 +65,11 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -66,13 +65,11 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
setTimeout(() => {
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check();
}, 100);
/* window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check();
});
}); */
});
@ -106,32 +103,28 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -106,32 +103,28 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
};
{
let categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"];
const categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"];
let divs: {
[category: string]: HTMLDivElement
} = {};
let keyCategory = Config.Emoji.keyCategory;
let sorted: {
[category: string]: any[]
[category: string]: string[]
} = {};
for(let unified in Config.Emoji.emoji) {
// @ts-ignore
let details = Config.Emoji.emoji[unified];
let category = details[keyCategory];
if(category == 'Symbols') category = 'Objects';
details.unified = unified;
for(let emoji in Config.Emoji) {
let details = Config.Emoji[emoji];
let i = '' + details;
let category = categories[+i[0] - 1];
if(!category) continue; // maybe it's skin tones
if(!sorted[category]) sorted[category] = [];
sorted[category][details.sort_order] = details;
sorted[category][+i.slice(1) || 0] = emoji;
}
//console.log('emoticons sorted:', sorted);
console.log('emoticons sorted:', sorted);
Object.keys(sorted).forEach(c => sorted[c].sort((a, b) => a - b));
//Object.keys(sorted).forEach(c => sorted[c].sort((a, b) => a - b));
categories.pop();
delete sorted["Skin Tones"];
@ -151,8 +144,8 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -151,8 +144,8 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
div.append(titleDiv, itemsDiv);
let emojis = sorted[category];
emojis.forEach(details => {
let emoji = details.unified;
emojis.forEach(emoji => {
//let emoji = details.unified;
//let emoji = (details.unified as string).split('-')
//.reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
@ -160,7 +153,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -160,7 +153,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let kek = RichTextProcessor.wrapRichText(emoji);
if(!kek.includes('emoji')) {
console.log(details, emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji));
console.log(emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji));
return;
}
@ -182,7 +175,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -182,7 +175,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let prevCategoryIndex = 1;
let menu = contentEmojiDiv.nextElementSibling.firstElementChild as HTMLUListElement;
let emojiScroll = new Scrollable(contentEmojiDiv, 'y', 500, 'EMOJI', null);
let emojiScroll = new Scrollable(contentEmojiDiv, 'y', 'EMOJI', null);
emojiScroll.container.addEventListener('scroll', (e) => {
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container);
});
@ -238,7 +231,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -238,7 +231,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let document = appDocsManager.getDoc(fileID);
if(document._ != 'documentEmpty') {
appMessagesManager.sendFile(appImManager.peerID, document, {isMedia: true});
appImManager.chatInputC.onMessageSent(true, false);
appImManager.chatInputC.onMessageSent(false);
dropdown.classList.remove('active');
toggleEl.classList.remove('active');
} else {
@ -303,8 +296,6 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -303,8 +296,6 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
if(prepend) stickersScroll.prepend(categoryDiv);
else stickersScroll.append(categoryDiv);
setTimeout(() => lazyLoadQueue.check(), 0);
/* let scrollHeight = categoryDiv.scrollHeight;
let prevHeight = heights[heights.length - 1] || 0;
//console.log('scrollHeight', scrollHeight, categoryDiv, stickersDiv.childElementCount);
@ -325,7 +316,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -325,7 +316,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.height + (i == 0 ? paddingTop : 0);
}); */
let concated = Array.from(stickersScroll.splitUp.children);
let concated = Array.from(stickersScroll.splitUp.children) as HTMLElement[];
concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.scrollHeight + (i == 0 ? paddingTop : 0);
});
@ -343,9 +334,8 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -343,9 +334,8 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
};
let prevCategoryIndex = 0;
let stickersScroll = new Scrollable(contentStickersDiv, 'y', 500, 'STICKERS', undefined, undefined, 2);
let stickersScroll = new Scrollable(contentStickersDiv, 'y', 'STICKERS', undefined, undefined, 2);
stickersScroll.container.addEventListener('scroll', (e) => {
lazyLoadQueue.check();
lottieLoader.checkAnimations();
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll);
@ -440,11 +430,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -440,11 +430,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
masonry.addEventListener('click', onMediaClick);
let scroll = new Scrollable(contentDiv, 'y', 500, 'GIFS', null);
scroll.container.addEventListener('scroll', (e) => {
lazyLoadQueue.check();
});
let scroll = new Scrollable(contentDiv, 'y', 'GIFS', null);
let width = 400;
let maxSingleWidth = width - 100;

79
src/components/lazyLoadQueue.ts

@ -1,5 +1,3 @@ @@ -1,5 +1,3 @@
import { isElementInViewport } from "../lib/utils";
type LazyLoadElement = {
div: HTMLDivElement,
load: () => Promise<any>,
@ -17,14 +15,31 @@ export default class LazyLoadQueue { @@ -17,14 +15,31 @@ export default class LazyLoadQueue {
private log = console.log.bind(console, '[LL]:');
private debug = false;
constructor(private parallelLimit = 5) {
private observer: IntersectionObserver;
constructor(private parallelLimit = 5) {
this.observer = new IntersectionObserver(entries => {
for(let entry of entries) {
if(entry.isIntersecting) {
let target = entry.target as HTMLElement;
for(let item of this.lazyLoadMedia) {
if(item.div == target) {
item.wasSeen = true;
this.processQueue(item);
break;
}
}
}
}
});
}
public clear() {
this.tempID--;
this.lazyLoadMedia.length = 0;
this.loadingMedia = 0;
this.observer.disconnect();
}
public length() {
@ -45,24 +60,13 @@ export default class LazyLoadQueue { @@ -45,24 +60,13 @@ export default class LazyLoadQueue {
this.unlockResolve = null;
}
public async processQueue(id?: number) {
public async processQueue(item?: LazyLoadElement) {
if(this.parallelLimit > 0 && this.loadingMedia >= this.parallelLimit) return;
let item: LazyLoadElement;
let index: number;
if(id !== undefined) item = this.lazyLoadMedia.splice(id, 1)[0];
else {
item = this.lazyLoadMedia.findAndSplice(i => isElementInViewport(i.div));
if(!item) {
let length = this.lazyLoadMedia.length;
for(index = length - 1; index >= 0; --index) {
if(this.lazyLoadMedia[index].wasSeen) {
item = this.lazyLoadMedia.splice(index, 1)[0];
break;
}
}
}
if(item) {
this.lazyLoadMedia.findAndSplice(i => i == item);
} else {
item = this.lazyLoadMedia.findAndSplice(i => i.wasSeen);
}
if(item) {
@ -73,17 +77,17 @@ export default class LazyLoadQueue { @@ -73,17 +77,17 @@ export default class LazyLoadQueue {
this.debug && this.log('will load media', this.lockPromise, item);
try {
if(this.lockPromise && false) {
if(this.lockPromise/* && false */) {
let perf = performance.now();
await this.lockPromise;
this.debug && this.log('waited lock:', performance.now() - perf);
}
await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
await item.load();
} catch(err) {
console.error('loadMediaQueue error:', err, item, id, index);
console.error('loadMediaQueue error:', err, item);
}
if(tempID == this.tempID) {
@ -98,39 +102,14 @@ export default class LazyLoadQueue { @@ -98,39 +102,14 @@ export default class LazyLoadQueue {
}
}
public check(id?: number) {
if(id !== undefined) {
let {div, wasSeen} = this.lazyLoadMedia[id];
if(!wasSeen && isElementInViewport(div)) {
//console.log('will load div by id:', div, div.getBoundingClientRect());
this.lazyLoadMedia[id].wasSeen = true;
this.processQueue(id);
}
return;
}
let length = this.lazyLoadMedia.length;
for(let i = length - 1; i >= 0; --i) {
let {div, wasSeen} = this.lazyLoadMedia[i];
if(!wasSeen && isElementInViewport(div)) {
//console.log('will load div:', div);
this.lazyLoadMedia[i].wasSeen = true;
this.processQueue(i);
//this.lazyLoadMedia.splice(i, 1);
}
}
}
public push(el: LazyLoadElement) {
let id = this.lazyLoadMedia.push(el) - 1;
this.lazyLoadMedia.push(el);
if(el.wasSeen) {
this.processQueue(id);
this.processQueue(el);
} else {
el.wasSeen = false;
this.check(id);
this.observer.observe(el.div);
}
}
}

420
src/components/scrollable copy.ts

@ -1,420 +0,0 @@ @@ -1,420 +0,0 @@
import { isElementInViewport, isScrolledIntoView, cancelEvent } from "../lib/utils";
export default class Scrollable {
public container: HTMLDivElement;
public thumb: HTMLDivElement;
public type: string;
public side: string;
public scrollType: string;
public scrollSide: string;
public clientAxis: string;
public scrollSize = -1;
public size = 0;
public thumbSize = 0;
public hiddenElements: {
up: {element: Element, height: number}[],
down: {element: Element, height: number}[]
} = {
up: [],
down: []
};
public paddings = {up: 0, down: 0};
public paddingTopDiv: HTMLDivElement;
public paddingBottomDiv: HTMLDivElement;
public splitUp: HTMLElement;
public splitOffset = 0;
public onAddedBottom: () => void = null;
public topObserver: IntersectionObserver;
public isTopIntersecting: boolean;
public bottomObserver: IntersectionObserver;
public isBottomIntersecting: boolean;
public splitObserver: IntersectionObserver;
constructor(public el: HTMLDivElement, x = false, y = true) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
let arr = [];
for(let i = 0.001; i < 1; i += 0.001) arr.push(i);
this.topObserver = new IntersectionObserver(entries => {
let entry = entries[0];
console.log('top intersection:', entries, this.isTopIntersecting, entry.isIntersecting, entry.intersectionRatio > 0);
if(this.isTopIntersecting = entry.isIntersecting) {
this.onTopIntersection(entry);
}
console.log('top intersection end');
}, {threshold: arr});
this.bottomObserver = new IntersectionObserver(entries => {
let entry = entries[0];
console.log('bottom intersection:', entries, this.isBottomIntersecting, entry.isIntersecting, entry.intersectionRatio > 0);
if(this.isBottomIntersecting = entry.isIntersecting) {
this.onBottomIntersection(entry);
if(this.onScrolledBottom) this.onScrolledBottom();
}
}, {threshold: arr});
this.splitObserver = new IntersectionObserver(entries => {
//console.log('splitObserver', entries);
for(let entry of entries) {
if(!entry.isIntersecting/* && entry.target.parentElement */) {
let child = entry.target;
console.log('onscroll entry', entry.boundingClientRect, child, entry);
let isTop = (entry.boundingClientRect.top + this.splitOffset) <= 0;
let isBottom = entry.rootBounds.height <= entry.boundingClientRect.top;
let height = child.scrollHeight;
let toPush = {element: child, height};
if(isTop) {
this.paddings.up += height;
this.hiddenElements.up.push(toPush);
child.parentElement.removeChild(child);
console.log('onscroll sliced up', child);
this.paddingTopDiv.style.height = this.paddings.up + 'px';
} else if(isBottom) {
this.paddings.down += height;
this.hiddenElements.down.unshift(toPush);
child.parentElement.removeChild(child);
console.log('onscroll sliced down', child);
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
}
//console.log('splitObserver', entry, entry.target, isTop);
}
}
});
if(x) {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
this.clientAxis = 'clientX';
let scrollHorizontally = (e: any) => {
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
this.container.scrollLeft -= (delta * 20);
e.preventDefault();
};
if(this.container.addEventListener) {
// IE9, Chrome, Safari, Opera
this.container.addEventListener("mousewheel", scrollHorizontally, false);
// Firefox
this.container.addEventListener("DOMMouseScroll", scrollHorizontally, false);
} else {
// IE 6/7/8
// @ts-ignore
this.container.attachEvent("onmousewheel", scrollHorizontally);
}
} else if(y) {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
this.clientAxis = 'clientY';
} else {
throw new Error('no side for scroll');
}
this.thumb = document.createElement('div');
this.thumb.className = 'scrollbar-thumb';
// @ts-ignore
this.thumb.style[this.type] = '30px';
let onMouseMove = (e: MouseEvent) => {
let rect = this.thumb.getBoundingClientRect();
let diff: number;
// @ts-ignore
diff = e[this.clientAxis] - rect[this.side];
// @ts-ignore
this.container[this.scrollSide] += diff * 0.5;
console.log('onMouseMove', e, diff);
cancelEvent(e);
};
this.thumb.addEventListener('mousedown', () => {
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', () => {
window.removeEventListener('mousemove', onMouseMove);
}, {once: true});
});
//this.container.addEventListener('mouseover', this.resize.bind(this)); // omg
window.addEventListener('resize', this.resize.bind(this));
this.paddingTopDiv = document.createElement('div');
this.paddingTopDiv.classList.add('scroll-padding');
this.paddingBottomDiv = document.createElement('div');
this.paddingBottomDiv.classList.add('scroll-padding');
this.topObserver.observe(this.paddingTopDiv);
this.bottomObserver.observe(this.paddingBottomDiv);
this.container.addEventListener('scroll', this.onScroll.bind(this));
//this.container.append(this.paddingTopDiv);
Array.from(el.children).forEach(c => this.container.append(c));
//this.container.append(this.paddingBottomDiv);
el.append(this.container);//container.append(el);
this.container.parentElement.append(this.thumb);
this.resize();
}
public resize() {
console.time('scroll resize');
// @ts-ignore
this.scrollSize = this.container[this.scrollType];
let rect = this.container.getBoundingClientRect();
// @ts-ignore
this.size = rect[this.type];
if(!this.size || this.size == this.scrollSize) {
this.thumbSize = 0;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
console.timeEnd('scroll resize');
return;
}
//if(!height) return;
let divider = this.scrollSize / this.size / 0.5;
this.thumbSize = this.size / divider;
if(this.thumbSize < 20) this.thumbSize = 20;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
console.timeEnd('scroll resize');
// @ts-ignore
//console.log('onresize', thumb.style[type], thumbHeight, height);
}
public setVirtualContainer(el?: HTMLElement) {
this.splitUp = el;
this.hiddenElements.up.length = this.hiddenElements.down.length = 0;
this.paddings.up = this.paddings.down = 0;
if(this.paddingTopDiv.parentElement) {
this.paddingTopDiv.style.height = '';
this.paddingBottomDiv.style.height = '';
}
/* this.topObserver.unobserve(this.paddingTopDiv);
this.bottomObserver.unobserve(this.paddingBottomDiv);
this.topObserver.observe(this.paddingTopDiv);
this.bottomObserver.observe(this.paddingBottomDiv); */
if(el) {
el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
}
}
public onScroll() {
// @ts-ignore
//let st = container[scrollSide];
//console.time('scroll onScroll');
// @ts-ignore
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
this.resize();
}
// @ts-ignore
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
let maxValue = 100 - (this.thumbSize / this.size * 100);
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
// @ts-ignore
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
//console.timeEnd('scroll onScroll');
}
public async onTopIntersection2(entry: IntersectionObserverEntry) {
console.log('onTopIntersection');
if(this.hiddenElements.up.length && this.paddings.up) {
//while(this.isTopIntersecting && this.paddings.up) {
let child = this.hiddenElements.up.pop();
console.log('top returning from hidden', child);
if(!child) {
this.paddings.up = 0;
this.paddingTopDiv.style.height = '0px';
return;
}
/* await new Promise((resolve, reject) => {
window.requestAnimationFrame(resolve);
}); */
this.splitUp.prepend(child.element);
this.paddings.up -= child.height;
this.paddingTopDiv.style.height = this.paddings.up + 'px';
//}
} else {
this.paddingTopDiv.style.height = '0px';
}
}
public async onTopIntersection(entry: IntersectionObserverEntry) {
console.log('onTopIntersection');
if(this.hiddenElements.up.length && this.paddings.up) {
let needHeight = entry.intersectionRect.height + this.splitOffset;
while(needHeight > 0 && this.paddings.up) {
let child = this.hiddenElements.up.pop();
console.log('top returning from hidden', child);
if(!child) {
this.paddings.up = 0;
this.paddingTopDiv.style.height = '0px';
break;
}
/* await new Promise((resolve, reject) => {
window.requestAnimationFrame(resolve);
}); */
this.splitUp.prepend(child.element);
let height = child.height || child.element.scrollHeight;
needHeight -= height;
this.paddings.up -= height;
this.paddingTopDiv.style.height = this.paddings.up + 'px';
}
} else {
this.paddingTopDiv.style.height = '0px';
}
}
public onBottomIntersection2() {
console.log('onBottomIntersection');
if(this.hiddenElements.down.length && this.paddings.down) {
//while(this.isBottomIntersecting && this.paddings.down) {
let child = this.hiddenElements.down.shift();
if(!child) {
this.paddings.down = 0;
this.paddingBottomDiv.style.height = '0px';
return;//break;
}
this.splitUp.append(child.element);
this.paddings.down -= child.height;
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
//}
if(this.onAddedBottom) this.onAddedBottom();
} else {
this.paddingBottomDiv.style.height = '0px';
}
}
public onBottomIntersection(entry: IntersectionObserverEntry) {
console.log('onBottomIntersection');
if(this.hiddenElements.down.length && this.paddings.down) {
let needHeight = entry.intersectionRect.height + this.splitOffset;
while(needHeight > 0 && this.paddings.down) {
let child = this.hiddenElements.down.shift();
if(!child) {
this.paddings.down = 0;
this.paddingBottomDiv.style.height = '0px';
break;
}
this.splitUp.append(child.element);
let height = child.height || child.element.scrollHeight;
needHeight -= height;
this.paddings.down -= height;
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
}
if(this.onAddedBottom) this.onAddedBottom();
} else {
this.paddingBottomDiv.style.height = '0px';
}
}
public onScrolledBottom() {
}
public splitAppend(...smth: (string | Node)[]) {
this.splitUp.append(...smth);
for(let node of smth) {
if(typeof(node) !== 'string') {
this.splitObserver.observe(node as Element);
}
}
}
set scrollTop(y: number) {
this.container.scrollTop = y;
}
get scrollTop() {
return this.container.scrollTop;
}
get scrollHeight() {
return this.container.scrollHeight;
}
get parentElement() {
return this.container.parentElement;
}
get offsetHeight() {
return this.container.offsetHeight;
}
}

139
src/components/scrollable_new.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { logger, deferredPromise, CancellablePromise } from "../lib/polyfill";
import { logger } from "../lib/polyfill";
import smoothscroll from '../lib/smoothscroll';
(window as any).__forceSmoothScrollPolyfill__ = true;
smoothscroll.polyfill();
@ -30,35 +30,23 @@ Array.from($0.querySelectorAll('.bubble__container')).forEach(_el => { @@ -30,35 +30,23 @@ Array.from($0.querySelectorAll('.bubble__container')).forEach(_el => {
export default class Scrollable {
public container: HTMLDivElement;
public type: string;
public side: string;
public translate: string;
public scrollType: string;
public scrollSide: string;
public clientAxis: string;
public clientSize: string;
public scrollSize = -1; // it will be scrollHeight
public size = 0; // it will be outerHeight of container (not scrollHeight)
public splitUp: HTMLElement;
public onScrolledTop: () => void = null;
public onScrolledBottom: () => void = null;
public onScrolledTopFired = false;
public onScrolledBottomFired = false;
public onScrollMeasure: number = null;
public lastScrollTop: number = 0;
public scrollTopOffset: number = 0;
private disableHoverTimeout: number = 0;
private log: ReturnType<typeof logger>;
private debug = false;
private measureMutex: CancellablePromise<void>;
private sentinelsObserver: IntersectionObserver;
private topSentinel: HTMLDivElement;
private bottomSentinel: HTMLDivElement;
private observer: IntersectionObserver;
private visible: Set<HTMLElement>;
@ -86,7 +74,7 @@ export default class Scrollable { @@ -86,7 +74,7 @@ export default class Scrollable {
this.visible.delete(element);
}
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', public splitOffset = 300, logPrefix = '', public appendTo = el, public onScrollOffset = splitOffset, public splitCount = 15) {
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', logPrefix = '', public appendTo = el, public onScrollOffset = 300, public splitCount = 15) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
@ -161,20 +149,10 @@ export default class Scrollable { @@ -161,20 +149,10 @@ export default class Scrollable {
}
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''));
this.measureMutex = deferredPromise<void>();
this.measureMutex.resolve();
if(axis == 'x') {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.translate = 'translateX';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
this.clientAxis = 'clientX';
this.clientSize = 'clientWidth';
let scrollHorizontally = (e: any) => {
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
@ -193,24 +171,11 @@ export default class Scrollable { @@ -193,24 +171,11 @@ export default class Scrollable {
}
} else if(axis == 'y') {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.translate = 'translateY';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
this.clientAxis = 'clientY';
this.clientSize = 'clientHeight';
} else {
throw new Error('no side for scroll');
}
//this.container.addEventListener('mouseover', this.resize.bind(this)); // omg
window.addEventListener('resize', () => {
window.requestAnimationFrame(() => {
this.onScroll();
});
});
window.addEventListener('resize', () => this.onScroll());
this.container.addEventListener('scroll', () => this.onScroll(), {passive: true, capture: true});
Array.from(el.children).forEach(c => this.container.append(c));
@ -219,12 +184,41 @@ export default class Scrollable { @@ -219,12 +184,41 @@ export default class Scrollable {
//this.onScroll();
}
public attachSentinels(container = this.container, offset = this.onScrollOffset) {
if(!this.sentinelsObserver) {
this.topSentinel = document.createElement('div');
this.topSentinel.classList.add('scrollable-sentinel');
this.topSentinel.style.top = offset + 'px';
this.bottomSentinel = document.createElement('div');
this.bottomSentinel.classList.add('scrollable-sentinel');
this.bottomSentinel.style.bottom = offset + 'px';
this.container.append(this.topSentinel, this.bottomSentinel);
this.sentinelsObserver = new IntersectionObserver(entries => {
for(let entry of entries) {
if(entry.isIntersecting) {
let top = entry.target == this.topSentinel;
if(top) {
this.onScrolledTop && this.onScrolledTop();
} else {
this.onScrolledBottom && this.onScrolledBottom();
}
}
}
});
this.sentinelsObserver.observe(this.topSentinel);
this.sentinelsObserver.observe(this.bottomSentinel);
}
container.prepend(this.topSentinel);
container.append(this.bottomSentinel);
}
public setVirtualContainer(el?: HTMLElement) {
this.splitUp = el;
this.onScrolledBottomFired = this.onScrolledTopFired = false;
this.lastScrollTop = 0;
this.log('setVirtualContainer:', el, this);
}
@ -250,52 +244,11 @@ export default class Scrollable { @@ -250,52 +244,11 @@ export default class Scrollable {
this.disableHoverTimeout = setTimeout(() => {
appendTo.classList.remove('disable-hover');
this.lastScrollDirection = 0;
if(!this.measureMutex.isFulfilled) {
this.measureMutex.resolve();
}
}, 100);
if(this.onScrollMeasure) return; //window.cancelAnimationFrame(this.onScrollMeasure);
this.onScrollMeasure = window.requestAnimationFrame(() => {
// @ts-ignore
let scrollPos = this.container[this.scrollSide];
//if(this.measureMutex.isFulfilled) {
// @ts-ignore quick brown fix
this.size = this.container[this.clientSize];
// @ts-ignore
let scrollSize = this.container[this.scrollType];
this.scrollSize = scrollSize;
//this.measureMutex = deferredPromise<void>();
//}
let scrollTop = scrollPos - this.scrollTopOffset;
let maxScrollTop = this.scrollSize - this.scrollTopOffset - this.size;
if(this.onScrolledBottom && !this.scrollLocked) {
if((maxScrollTop - scrollTop) <= this.onScrollOffset) {
//if(!this.onScrolledBottomFired) {
this.onScrolledBottomFired = true;
this.onScrolledBottom();
//}
} else {
this.onScrolledBottomFired = false;
}
}
if(this.onScrolledTop && !this.scrollLocked) {
//this.log('onScrolledTop:', scrollTop, this.onScrollOffset);
if(scrollTop <= this.onScrollOffset) {
this.onScrolledTopFired = true;
this.onScrolledTop();
} else {
this.onScrolledTopFired = false;
}
}
if(!this.splitUp || this.onScrollMeasure) return;
this.onScrollMeasure = window.requestAnimationFrame(() => {
let scrollTop = this.container.scrollTop;
if(this.lastScrollTop != scrollTop) {
this.lastScrollDirection = this.lastScrollTop < scrollTop ? 1 : -1;
this.lastScrollTop = scrollTop;
@ -385,8 +338,8 @@ export default class Scrollable { @@ -385,8 +338,8 @@ export default class Scrollable {
}
}
public scrollTo(top: number, smooth = true) {
if(this.scrollLocked) return;
public scrollTo(top: number, smooth = true, important = false) {
if(this.scrollLocked && !important) return;
let scrollTop = this.scrollTop;
if(scrollTop == Math.floor(top)) {

270
src/components/scrollable_spliceCount.ts

@ -1,270 +0,0 @@ @@ -1,270 +0,0 @@
import { isElementInViewport } from "../lib/utils";
export default class Scrollable {
public container: HTMLDivElement;
public thumb: HTMLDivElement;
public type: string;
public side: string;
public scrollType: string;
public scrollSide: string;
public scrollSize = -1;
public size = 0;
public thumbSize = 0;
public hiddenElements: {
up: Element[],
down: Element[]
} = {
up: [],
down: []
};
public paddings = {up: 0, down: 0};
public paddingTopDiv: HTMLDivElement;
public paddingBottomDiv: HTMLDivElement;
public splitUp: HTMLElement;
public spliceCount = 1;
public useStylePadding = false;
public useOneHeight = false;
constructor(public el: HTMLDivElement, x = false, y = true) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
if(x) {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
} else if(y) {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
} else {
throw new Error('no side for scroll');
}
this.thumb = document.createElement('div');
this.thumb.className = 'scrollbar-thumb';
// @ts-ignore
this.thumb.style[this.type] = '30px';
this.container.addEventListener('mouseover', this.resize.bind(this));
window.addEventListener('resize', this.resize.bind(this));
this.paddingTopDiv = document.createElement('div');
this.paddingTopDiv.classList.add('scroll-padding');
this.paddingBottomDiv = document.createElement('div');
this.paddingBottomDiv.classList.add('scroll-padding');
this.container.addEventListener('scroll', this.onScroll.bind(this));
this.container.append(this.paddingTopDiv);
Array.from(el.children).forEach(c => this.container.append(c));
this.container.append(this.paddingBottomDiv);
el.append(this.container);//container.append(el);
this.container.parentElement.append(this.thumb);
this.resize();
}
public resize() {
// @ts-ignore
this.scrollSize = this.container[this.scrollType];
let rect = this.container.getBoundingClientRect();
// @ts-ignore
this.size = rect[this.type];
if(!this.size || this.size == this.scrollSize) {
this.thumbSize = 0;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
return;
}
//if(!height) return;
let divider = this.scrollSize / this.size / 0.5;
this.thumbSize = this.size / divider;
if(this.thumbSize < 20) this.thumbSize = 20;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
// @ts-ignore
//console.log('onresize', thumb.style[type], thumbHeight, height);
}
public setVirtualContainer(el: HTMLElement, spliceCount = 1, useStylePadding = false, useOneHeight = false) {
this.splitUp = el;
this.hiddenElements = {
up: [],
down: []
};
this.paddings = {
up: 0,
down: 0
};
this.spliceCount = spliceCount;
this.useStylePadding = useStylePadding;
this.useOneHeight = useOneHeight;
if(this.paddingTopDiv.parentElement) {
this.paddingTopDiv.style.height = '';
this.paddingBottomDiv.style.height = '';
}
/* if(useStylePadding) {
this.paddingTopDiv.parentElement.removeChild(this.paddingTopDiv);
this.paddingBottomDiv.parentElement.removeChild(this.paddingBottomDiv);
} else { */
el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
//}
if(useStylePadding) {
this.paddingTopDiv.style.height = '10px';
this.paddingBottomDiv.style.height = '10px';
}
}
public onScroll() {
// @ts-ignore
//let st = container[scrollSide];
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
this.resize();
}
// @ts-ignore
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
let maxValue = 100 - (this.thumbSize / this.size * 100);
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
// @ts-ignore
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
if(!this.splitUp) {
return;
}
let splitUp = this.splitUp;
let children = Array.from(splitUp.children) as HTMLElement[];
let firstVisible = -1, lastVisible = -1;
let length = children.length;
for(let i = 0; i < length; ++i) {
let child = children[i];
if(isElementInViewport(child)) {
if(firstVisible < 0) firstVisible = i;
lastVisible = i;
}
}
console.log('onscroll', firstVisible, lastVisible);
if(firstVisible > 0) {
let sliced = children.slice(0, firstVisible);
let height = 0, singleHeight = sliced[0].scrollHeight;
for(let child of sliced) {
height += child.scrollHeight;
this.hiddenElements.up.push(child);
child.parentElement.removeChild(child);
}
this.paddings.up += this.useOneHeight ? singleHeight : height;
//console.log('sliced up', sliced.length);
//sliced.forEach(child => child.style.display = 'none');
if(this.useStylePadding) splitUp.style.paddingTop = this.paddings.up + 'px';
else this.paddingTopDiv.style.height = this.paddings.up + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.up.length) {
console.log('onscroll up', isElementInViewport(this.paddingTopDiv), this.paddings.up);
while(isElementInViewport(this.paddingTopDiv) && this.paddings.up) {
//let child = this.hiddenElements.up.pop();
/*
splitUp.prepend(...childs);
this.paddings.up -= child.scrollHeight;
this.paddingTopDiv.style.height = this.paddings.up + 'px';*/
let childs = this.hiddenElements.up.splice(-this.spliceCount).reverse();
let height = 0;
for(let child of childs) {
splitUp.prepend(child);
height += child.scrollHeight;
}
this.paddings.up -= this.useOneHeight ? childs[0].scrollHeight : height;
if(this.useStylePadding) splitUp.style.paddingTop = this.paddings.up + 'px';
else this.paddingTopDiv.style.height = this.paddings.up + 'px';
}
}
if(lastVisible < (length - 1)) {
let sliced = children.slice(lastVisible + 1, this.useOneHeight ? lastVisible + 1 + this.spliceCount : undefined).reverse();
let height = 0, singleHeight = sliced[0].scrollHeight;
for(let child of sliced) {
height += child.scrollHeight;
this.hiddenElements.down.unshift(child);
child.parentElement.removeChild(child);
}
this.paddings.down += this.useOneHeight ? singleHeight : height;
console.log('onscroll sliced down', splitUp, sliced.length, this.paddings.down + 'px');
//sliced.forEach(child => child.style.display = 'none');
/* if(this.useStylePadding) splitUp.style.paddingBottom = this.paddings.down + 'px';
else */ this.paddingBottomDiv.style.height = this.paddings.down + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.down.length) {
console.log('onscroll down', isElementInViewport(this.paddingBottomDiv), this.paddings.down, this.hiddenElements);
while(isElementInViewport(this.paddingBottomDiv) && this.paddings.down) {
/* let child = this.hiddenElements.down.shift();
splitUp.append(child);
this.paddings.down -= child.scrollHeight;
this.paddingBottomDiv.style.height = this.paddings.down + 'px'; */
let childs = this.hiddenElements.down.splice(0, this.spliceCount);
let height = 0;
for(let child of childs) {
splitUp.append(child);
height += child.scrollHeight;
}
this.paddings.down -= this.useOneHeight ? childs[0].scrollHeight : height;
/* if(this.useStylePadding) splitUp.style.paddingBottom = this.paddings.down + 'px';
else */ this.paddingBottomDiv.style.height = this.paddings.down + 'px';
}
}
//console.log('onscroll', container, firstVisible, lastVisible, hiddenElements);
//lastScrollPos = st;
}
}

11
src/components/wrappers.ts

@ -165,6 +165,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -165,6 +165,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
if(doc.type == 'gif') {
video.autoplay = true;
video.loop = true;
video.play();
} else if(doc.type == 'round') {
//video.dataset.ckin = doc.type == 'round' ? 'circle' : 'default';
video.dataset.ckin = 'circle';
@ -430,8 +431,9 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem @@ -430,8 +431,9 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem
let index = 0;
let skipped = 0;
let h = '';
for(let uint8 of wave) {
if (index > 0 && index % 4 == 0) {
if(index > 0 && index % 4 == 0) {
++index;
++skipped;
continue;
@ -443,12 +445,13 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem @@ -443,12 +445,13 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem
height = 2;
}
svg.insertAdjacentHTML('beforeend', `
h += `
<rect x="${(index - skipped) * 4}" y="${23 - height}" width="2" height="${height}" rx="1" ry="1"></rect>
`);
`;
++index;
}
svg.insertAdjacentHTML('beforeend', h);
let progress = div.querySelector('.audio-waveform') as HTMLDivElement;
@ -772,7 +775,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -772,7 +775,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
console.timeEnd('render sticker' + doc.id);
if(div.firstElementChild && div.firstElementChild.tagName != 'CANVAS') {
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') {
div.firstElementChild.remove();
}

2
src/countries.json

File diff suppressed because one or more lines are too long

2
src/emoji.json

File diff suppressed because one or more lines are too long

59
src/format_jsons.js

@ -2,7 +2,7 @@ let emoji = require('./emoji_pretty.json'); @@ -2,7 +2,7 @@ let emoji = require('./emoji_pretty.json');
//let countries = require('./countries_pretty.json');
let countries = require('fs').readFileSync('./countries.dat').toString();
console.log(countries);
//console.log(countries);
//console.log(emoji, countries);
@ -23,7 +23,7 @@ let formatted = emoji.filter(e => e.has_img_apple); @@ -23,7 +23,7 @@ let formatted = emoji.filter(e => e.has_img_apple);
require('fs').writeFileSync('./emoji.json', JSON.stringify(formatted)); */
{
if(false) {
let obj = {};
formatted.forEach(e => {
let {unified, name, short_names, category, sheet_x, sheet_y, sort_order} = e;
@ -45,6 +45,59 @@ require('fs').writeFileSync('./emoji.json', JSON.stringify(formatted)); */ @@ -45,6 +45,59 @@ require('fs').writeFileSync('./emoji.json', JSON.stringify(formatted)); */
require('fs').writeFileSync('./emoji.json', JSON.stringify(obj));
}
{
let categories = {
"Smileys & Emotion": 1
, "Animals & Nature": 2
, "Food & Drink": 3
, "Travel & Places": 4
, "Activities": 5
, "Objects": 6
, "Symbols": 6
, "Flags": 7
, "Skin Tones": 8
};
let maxObjectsIndex = -1;
formatted.forEach(e => {
if(e.category == 'Objects') {
if(e.sort_order > maxObjectsIndex) {
maxObjectsIndex = e.sort_order;
}
}
});
formatted.forEach(e => {
if(e.category == 'Symbols') {
e.sort_order += maxObjectsIndex;
}
});
formatted.forEach(e => {
if(e.skin_variations) {
for(let i in e.skin_variations) {
formatted.push(e.skin_variations[i]);
}
}
});
let obj = {};
formatted.forEach(e => {
let {unified, name, short_names, category, sheet_x, sheet_y, sort_order} = e;
let emoji = unified.replace(/-FE0F/gi, '').split('-')
.reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
let c = categories[category] === undefined ? 9 : categories[category];
//obj[emoji] = '' + c + sort_order;
//obj[emoji] = +('' + (c * 1000 + sort_order)).replace(/0+/g, '0').replace(/^(\d)0(\d)/g, '$1$2');
obj[emoji] = e.sort_order !== undefined ? +('' + c + sort_order) : 0;
});
console.log(obj);
require('fs').writeFileSync('./emoji.json', JSON.stringify(obj));
}
/* {
let obj = {};
formatted.forEach(e => {
@ -89,7 +142,7 @@ require('fs').writeFileSync('./emoji.json', JSON.stringify(formatted)); */ @@ -89,7 +142,7 @@ require('fs').writeFileSync('./emoji.json', JSON.stringify(formatted)); */
};
arr.push(item);
console.log(item);
//console.log(item);
});
require('fs').writeFileSync('./countries.json', JSON.stringify(arr));

2
src/lib/appManagers/apiUpdatesManager.ts

@ -499,7 +499,7 @@ export class ApiUpdatesManager { @@ -499,7 +499,7 @@ export class ApiUpdatesManager {
this.attached = true;
apiManager.setUpdatesProcessor(this.processUpdateMessage.bind(this));
if(!state) {
if(!state || !state.pts || !state.date || !state.seq) {
apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult: any) => {
this.updatesState.seq = stateResult.seq;
this.updatesState.pts = stateResult.pts;

74
src/lib/appManagers/appDialogsManager.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { findUpClassName, $rootScope, escapeRegExp, whichChild, findUpTag } from "../utils";
import { findUpClassName, $rootScope, escapeRegExp, whichChild, findUpTag, cancelEvent } from "../utils";
import appImManager, { AppImManager } from "./appImManager";
import appPeersManager from './appPeersManager';
import appMessagesManager, { AppMessagesManager, Dialog } from "./appMessagesManager";
@ -401,30 +401,20 @@ export class AppDialogsManager { @@ -401,30 +401,20 @@ export class AppDialogsManager {
constructor() {
this.chatsPreloader = putPreloader(null, true);
//this.chatsContainer.append(this.chatsPreloader);
this.pinnedDelimiter = document.createElement('div');
this.pinnedDelimiter.classList.add('pinned-delimiter');
this.pinnedDelimiter.appendChild(document.createElement('span'));
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
let splitOffset = 1110;
this.scroll = new Scrollable(this.chatsContainer, 'y', splitOffset, 'CL', this.chatList, 500);
this.scroll = new Scrollable(this.chatsContainer, 'y', 'CL', this.chatList, 500);
this.scroll.setVirtualContainer(this.chatList);
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
/* this.chatsHidden = this.scroll.hiddenElements;
this.chatsVisible = this.scroll.visibleElements; */
this.scroll.attachSentinels();
this.scrollArchived = new Scrollable(this.chatsArchivedContainer, 'y', splitOffset, 'CLA', this.chatListArchived, 500);
this.scrollArchived = new Scrollable(this.chatsArchivedContainer, 'y', 'CLA', this.chatListArchived, 500);
this.scrollArchived.setVirtualContainer(this.chatListArchived);
this.scrollArchived.onScrolledBottom = this.onChatsArchivedScroll.bind(this);
/* this.chatsArchivedHidden = this.scrollArchived.hiddenElements;
this.chatsArchivedVisible = this.scrollArchived.visibleElements; */
//this.scrollArchived.container.addEventListener('scroll', this.onChatsArchivedScroll.bind(this));
//let chatClosedDiv = document.getElementById('chat-closed');
this.scroll.attachSentinels();
this.setListClickListener(this.chatList);
this.setListClickListener(this.chatListArchived);
@ -611,11 +601,6 @@ export class AppDialogsManager { @@ -611,11 +601,6 @@ export class AppDialogsManager {
else this.loadedAll = true;
}
/* if(archived) {
let count = result.count;
this.archivedCount.innerText = '' + count;
} */
this.log('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.scroll.length, archived);
this.scroll.onScroll();
} catch(err) {
@ -627,20 +612,21 @@ export class AppDialogsManager { @@ -627,20 +612,21 @@ export class AppDialogsManager {
}
public onChatsScroll() {
if(this.loadedAll /* || this.scroll.hiddenElements.down.length > 0 */ || this.loadDialogsPromise/* || 1 == 1 */) return;
if(this.loadedAll || this.loadDialogsPromise) return;
this.loadDialogs();
}
public onChatsArchivedScroll() {
if(this.loadedArchivedAll /* || this.scrollArchived.hiddenElements.down.length > 0 */ || this.loadDialogsPromise/* || 1 == 1 */) return;
if(this.loadedArchivedAll || this.loadDialogsPromise) return;
this.loadDialogs(true);
}
public setListClickListener(list: HTMLUListElement, onFound?: () => void) {
list.addEventListener('click', (e: Event) => {
//return;
cancelEvent(e);
console.log('dialogs click list');
let target = e.target as HTMLElement;
let elem = target.classList.contains('rp') ? target : findUpClassName(target, 'rp');
@ -657,14 +643,9 @@ export class AppDialogsManager { @@ -657,14 +643,9 @@ export class AppDialogsManager {
this.lastActiveListElement.classList.remove('active');
}
let startTime = Date.now();
let result: ReturnType<AppImManager['setPeer']>;
//console.log('appDialogsManager: lock lazyLoadQueue');
if(elem) {
/* if(chatClosedDiv) {
chatClosedDiv.style.display = 'none';
} */
if(onFound) onFound();
let peerID = +elem.getAttribute('data-peerID');
@ -677,38 +658,14 @@ export class AppDialogsManager { @@ -677,38 +658,14 @@ export class AppDialogsManager {
result = appImManager.setPeer(peerID, lastMsgID);
if(result instanceof Promise) {
/* if(result instanceof Promise) {
this.lastGoodClickID = this.lastClickID;
appImManager.lazyLoadQueue.lock();
}
} else /* if(chatClosedDiv) */ {
} */
} else {
result = appImManager.setPeer(0);
//chatClosedDiv.style.display = '';
}
/* if(!(result instanceof Promise)) { // if click on same dialog
this.rippleCallback();
this.rippleCallback = null;
} */
/* promise.then(() => {
appImManager.lazyLoadQueue.unlock();
}); */
/* promise.then(() => {
let length = appImManager.lazyLoadQueue.length();
console.log('pre ripple callback', length);
if(length) {
setTimeout(() => {
this.rippleCallback();
}, length * 25);
} else {
let elapsedTime = Date.now() - startTime;
this.rippleCallback(elapsedTime > 200);
}
}); */
});
}, {capture: true});
}
public setDialogPosition(dialog: Dialog) {
@ -1012,7 +969,8 @@ export class AppDialogsManager { @@ -1012,7 +969,8 @@ export class AppDialogsManager {
paddingDiv.append(avatarEl, captionDiv);
if(rippleEnabled) {
ripple(paddingDiv, (id) => {
ripple(paddingDiv);
/* ripple(paddingDiv, (id) => {
this.log('dialogs click element');
this.lastClickID = id;
@ -1026,7 +984,7 @@ export class AppDialogsManager { @@ -1026,7 +984,7 @@ export class AppDialogsManager {
if(id == this.lastGoodClickID) {
appImManager.lazyLoadQueue.unlock();
}
});
}); */
}

152
src/lib/appManagers/appImManager.ts

@ -154,8 +154,15 @@ export class AppImManager { @@ -154,8 +154,15 @@ export class AppImManager {
// will call when message is sent (only 1)
$rootScope.$on('history_append', (e: CustomEvent) => {
let details = e.detail;
this.renderNewMessagesByIDs([details.messageID]);
if(!this.scrolledAllDown) {
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
if(dialog) {
this.setPeer(this.peerID, dialog.top_message);
}
} else {
this.renderNewMessagesByIDs([details.messageID], true);
}
});
// will call when sent for update pos
@ -411,7 +418,7 @@ export class AppImManager { @@ -411,7 +418,7 @@ export class AppImManager {
if(['IMG', 'DIV'].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV');
if(target.tagName == 'DIV' || target.tagName == "AVATAR-ELEMENT") {
if(target.classList.contains('forward')) {
if(target.classList.contains('goto-original')) {
let savedFrom = bubble.dataset.savedFrom;
let splitted = savedFrom.split('_');
let peerID = +splitted[0];
@ -685,7 +692,7 @@ export class AppImManager { @@ -685,7 +692,7 @@ export class AppImManager {
}
//appMessagesManager.readMessages(readed);
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
false && appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, min);
});
@ -793,7 +800,7 @@ export class AppImManager { @@ -793,7 +800,7 @@ export class AppImManager {
if(this.isScrollingTimeout) {
clearTimeout(this.isScrollingTimeout);
} else {
} else if(this.chatInner.classList.contains('is-scrolling')) {
this.chatInner.classList.add('is-scrolling');
}
@ -815,14 +822,14 @@ export class AppImManager { @@ -815,14 +822,14 @@ export class AppImManager {
}
public setScroll() {
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 750, 'IM', this.chatInner/* 1500 */, 300);
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner);
this.scroll = this.scrollable.container;
this.bubblesContainer.append(this.goDownBtn);
//this.scrollable.setVirtualContainer(this.chatInner);
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
this.scrollable.attachSentinels(undefined, 300);
this.scroll.addEventListener('scroll', this.onScroll.bind(this));
this.scroll.parentElement.classList.add('scrolled-down');
@ -943,8 +950,8 @@ export class AppImManager { @@ -943,8 +950,8 @@ export class AppImManager {
this.peerChanged = false;
this.firstUnreadBubble = null;
/* this.messagesQueue.length = 0;
this.messagesQueuePromise = null; */
this.messagesQueue.length = 0;
this.messagesQueuePromise = null;
lottieLoader.checkAnimations(false, 'chat', true);
@ -1028,10 +1035,14 @@ export class AppImManager { @@ -1028,10 +1035,14 @@ export class AppImManager {
const maxBubbleID = samePeer && Math.max(...Object.keys(this.bubbles).map(mid => +mid));
//let oldChatInner = this.chatInner;
this.cleanup();
this.chatInner = document.createElement('div');
this.chatInner.id = 'bubbles-inner';
this.scrollable.appendTo = this.chatInner;
this.chatInner.classList.add('disable-hover', 'is-scrolling');
this.lazyLoadQueue.lock();
let {promise, cached} = this.getHistory(lastMsgID, true, isJump, additionMsgID);
@ -1040,6 +1051,7 @@ export class AppImManager { @@ -1040,6 +1051,7 @@ export class AppImManager {
// clear
if(!cached) {
this.scrollable.container.innerHTML = '';
//oldChatInner.remove();
this.finishPeerChange();
this.preloader.attach(this.bubblesContainer);
}
@ -1052,12 +1064,17 @@ export class AppImManager { @@ -1052,12 +1064,17 @@ export class AppImManager {
if(cached) {
this.scrollable.container.innerHTML = '';
//oldChatInner.remove();
this.finishPeerChange();
} else {
this.preloader.detach();
}
this.scrollable.container.append(this.chatInner);
this.scrollable.attachSentinels();
//this.scrollable.container.insertBefore(this.chatInner, this.scrollable.container.lastElementChild);
this.lazyLoadQueue.unlock();
if(dialog && lastMsgID && lastMsgID != dialog.top_message && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) {
if(this.scrollable.scrollLocked) {
@ -1086,35 +1103,7 @@ export class AppImManager { @@ -1086,35 +1103,7 @@ export class AppImManager {
this.scrolledAllDown = true;
}
//this.chatInner.style.visibility = '';
/* let promises: Promise<any>[] = [];
for(let i in this.bubbles) {
(Array.from(this.bubbles[i].querySelectorAll('img, image, video')) as HTMLImageElement[]).forEach(el => {
promises.push(new Promise((resolve, reject) => {
if(el.tagName == 'VIDEO') {
el.onloadeddata = () => {
console.log('onloadeddata');
resolve();
};
} else {
el.onload = resolve;
}
}));
});
}
Promise.all(promises).then(() => {
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
//this.chatInner.style.visibility = '';
let parent = oldChatInner.parentElement;
oldChatInner.remove();
parent.append(this.chatInner);
this.scrollable.scrollTop = this.scrollable.scrollHeight;
});
});
}); */
this.log('scrolledAllDown:', this.scrolledAllDown);
console.timeEnd('appImManager setPeer');
@ -1126,6 +1115,7 @@ export class AppImManager { @@ -1126,6 +1115,7 @@ export class AppImManager {
]).catch(err => {
this.log.error('setPeer promises error:', err);
this.preloader.detach();
//oldChatInner.remove();
return false;
}).then(res => {
if(this.peerID == peerID) {
@ -1162,7 +1152,7 @@ export class AppImManager { @@ -1162,7 +1152,7 @@ export class AppImManager {
this.topbar.style.display = '';
if(appPeersManager.isAnyGroup(peerID)) this.chatInner.classList.add('is-chat');
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');
@ -1238,13 +1228,12 @@ export class AppImManager { @@ -1238,13 +1228,12 @@ export class AppImManager {
this.deleteEmptyDateGroups();
}
public renderNewMessagesByIDs(msgIDs: number[]) {
public renderNewMessagesByIDs(msgIDs: number[], scrolledDown = this.scrolledDown) {
if(!this.scrolledAllDown) { // seems search active or sliced
//////this.log('seems search is active, skipping render:', msgIDs);
this.log('seems search is active, skipping render:', msgIDs);
return;
}
let scrolledDown = this.scrolledDown;
msgIDs.forEach((msgID: number) => {
let message = appMessagesManager.getMessage(msgID);
@ -1256,8 +1245,14 @@ export class AppImManager { @@ -1256,8 +1245,14 @@ export class AppImManager {
//if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight;
if(this.messagesQueuePromise && scrolledDown) {
this.scrollable.scrollTo(this.scrollable.scrollHeight - 1, false, true);
this.messagesQueuePromise.then(() => {
this.scrollable.scrollTo(this.scrollable.scrollHeight, false);
this.log('messagesQueuePromise after:', this.chatInner.childElementCount, this.scrollable.scrollHeight);
this.scrollable.scrollTo(this.scrollable.scrollHeight, true, true);
setTimeout(() => {
this.log('messagesQueuePromise afterafter:', this.chatInner.childElementCount, this.scrollable.scrollHeight);
}, 10);
});
}
}
@ -1376,7 +1371,7 @@ export class AppImManager { @@ -1376,7 +1371,7 @@ export class AppImManager {
this.messagesQueue.length = 0;
let promises = queue.reduce((acc, {promises}) => acc.concat(promises), []);
console.log('promises to call', promises, queue);
//console.log('promises to call', promises, queue);
Promise.all(promises).then(() => {
if(this.chatInner != chatInner) {
this.log.warn('chatInner changed!', this.chatInner, chatInner);
@ -2010,35 +2005,44 @@ export class AppImManager { @@ -2010,35 +2005,44 @@ export class AppImManager {
let isHidden = message.fwd_from && !message.fwd_from.from_id && !message.fwd_from.channel_id;
if(isHidden) {
///////this.log('message to render hidden', message);
title = message.fwd_from.from_name;
title = RichTextProcessor.wrapEmojiText(message.fwd_from.from_name);
//title = message.fwd_from.from_name;
bubble.classList.add('hidden-profile');
}
//this.log(title);
if(message.fwdFromID || message.fwd_from) {
bubble.classList.add('forwarded');
if((message.fwdFromID || message.fwd_from)) {
if(this.peerID != this.myID) {
bubble.classList.add('forwarded');
}
if(message.savedFrom) {
let fwd = document.createElement('div');
fwd.classList.add('forward'/* , 'tgico-forward' */);
fwd.innerHTML = `
let goto = document.createElement('div');
goto.classList.add('goto-original', 'tgico-next');
/* fwd.innerHTML = `
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<defs>
<path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path>
</defs>
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
</svg>`;
bubbleContainer.append(fwd);
</svg>`; */
bubbleContainer.append(goto);
bubble.dataset.savedFrom = message.savedFrom;
}
if(!bubble.classList.contains('sticker')) {
let nameDiv = document.createElement('div');
nameDiv.classList.add('name');
nameDiv.innerHTML = 'Forwarded from ' + title;
nameDiv.dataset.peerID = message.fwdFromID;
//nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
if(this.peerID == this.myID) {
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fwdFromID, false);
nameDiv.innerHTML = title;
} else {
nameDiv.innerHTML = 'Forwarded from ' + title;
}
bubbleContainer.append(nameDiv);
}
} else {
@ -2079,19 +2083,18 @@ export class AppImManager { @@ -2079,19 +2083,18 @@ export class AppImManager {
}
}
if(!our && this.peerID < 0 && (!appPeersManager.isChannel(this.peerID) || appPeersManager.isMegagroup(this.peerID))) {
if((!our && this.peerID < 0 && (!appPeersManager.isChannel(this.peerID) || appPeersManager.isMegagroup(this.peerID)))
|| (this.peerID == this.myID && !message.reply_to_mid)) {
let avatarElem = new AvatarElement();
avatarElem.classList.add('user-avatar');
avatarElem.setAttribute('peer', '' + (message.fromID || 0));
if(!message.fromID && message.fwd_from && message.fwd_from.from_name) {
avatarElem.setAttribute('peer-title', message.fwd_from.from_name);
}
avatarElem.setAttribute('peer', '' + ((message.fwd_from ? message.fwdFromID : message.fromID) || 0));
/////////this.log('exec loadDialogPhoto', message);
/* if(message.fromID) { // if no - user hidden
appProfileManager.putPhoto(avatarDiv, message.fromID);
} else if(!title && message.fwd_from && message.fwd_from.from_name) {
title = message.fwd_from.from_name;
appProfileManager.putPhoto(avatarDiv, 0, false);
} */
this.log('exec loadDialogPhoto', message);
bubbleContainer.append(avatarElem);
}
@ -2099,7 +2102,7 @@ export class AppImManager { @@ -2099,7 +2102,7 @@ export class AppImManager {
bubble.classList.add('hide-name');
}
bubble.classList.add(our ? 'is-out' : 'is-in');
bubble.classList.add(our && (!message.fwd_from || this.peerID != this.myID) ? 'is-out' : 'is-in');
if(updatePosition) {
this.bubbleGroups.addBubble(bubble, message, reverse);
@ -2136,6 +2139,16 @@ export class AppImManager { @@ -2136,6 +2139,16 @@ export class AppImManager {
}
} */
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
if(dialog && dialog.top_message) {
for(let mid of history) {
if(mid == dialog.top_message) {
this.scrolledAllDown = true;
break;
}
}
}
console.time('appImManager render history');
let firstLoad = !!this.setPeerPromise && false;
@ -2199,7 +2212,7 @@ export class AppImManager { @@ -2199,7 +2212,7 @@ export class AppImManager {
let loadCount = realLoadCount;
if(testScroll) {
loadCount = 1;
//loadCount = 1;
if(Object.keys(this.bubbles).length > 0)
return {cached: false, promise: Promise.resolve(true)};
}
@ -2217,11 +2230,6 @@ export class AppImManager { @@ -2217,11 +2230,6 @@ export class AppImManager {
}
let result = appMessagesManager.getHistory(this.peerID, maxID, loadCount, backLimit);
/* if(!(result instanceof Promise)) {
let _result = result;
$rootScope.$broadcast('history_request'); // for ripple
result = new Promise((resolve, reject) => setTimeout(() => resolve(_result), 150));
} */
let promise: Promise<boolean>, cached: boolean;
if(result instanceof Promise) {

9
src/lib/appManagers/appMediaViewer.ts

@ -362,6 +362,7 @@ export class AppMediaViewer { @@ -362,6 +362,7 @@ export class AppMediaViewer {
if(aspecter) {
this.setFullAspect(aspecter, containerRect, rect);
aspecter.classList.add('disable-hover');
}
setTimeout(() => {
@ -379,6 +380,8 @@ export class AppMediaViewer { @@ -379,6 +380,8 @@ export class AppMediaViewer {
mover.classList.remove('active');
aspecter.style.cssText = '';
void mover.offsetLeft; // reflow
aspecter.classList.remove('disable-hover');
}
mover.classList.add('active');
@ -727,7 +730,11 @@ export class AppMediaViewer { @@ -727,7 +730,11 @@ export class AppMediaViewer {
}
if(!video.parentElement) {
aspecter.prepend(video);
if(aspecter.classList.contains('media-viewer-aspecter')) {
aspecter.prepend(video);
} else {
mover.prepend(video);
}
}
}

58
src/lib/appManagers/appMessagesManager.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { $rootScope, copy, tsNow, safeReplaceObject, dT, _, listMergeSorted, deepEqual, langPack } from "../utils";
import { $rootScope, copy, tsNow, safeReplaceObject, dT, listMergeSorted, deepEqual, langPack } from "../utils";
import appMessagesIDsManager from "./appMessagesIDsManager";
import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager";
@ -29,7 +29,10 @@ export type HistoryStorage = { @@ -29,7 +29,10 @@ export type HistoryStorage = {
count: number | null,
history: number[],
pending: number[],
readPromise?: any
readPromise?: any,
maxOutID?: number,
reply_markup?: any
};
export type HistoryResult = {
@ -189,6 +192,19 @@ export class AppMessagesManager { @@ -189,6 +192,19 @@ export class AppMessagesManager {
}
if(messages) {
/* let tempID = this.tempID;
for(let message of messages) {
if(message.id < tempID) {
tempID = message.id;
}
}
if(tempID != this.tempID) {
console.log('Set tempID to:', tempID);
this.tempID = tempID;
} */
this.saveMessages(messages);
}
@ -208,7 +224,7 @@ export class AppMessagesManager { @@ -208,7 +224,7 @@ export class AppMessagesManager {
}).catch(resolve);
});
setInterval(() => this.saveState(), 10000);
//setInterval(() => this.saveState(), 10000);
}
public saveState() {
@ -218,15 +234,30 @@ export class AppMessagesManager { @@ -218,15 +234,30 @@ export class AppMessagesManager {
for(let folderID in this.dialogsStorage) {
for(let dialog of this.dialogsStorage[folderID]) {
let message = this.getMessage(dialog.top_message);
if(message._ != 'messageEmpty' && message.id > 0) {
messages.push(message);
let historyStorage = this.historiesStorage[dialog.peerID];
let history = [].concat(historyStorage?.pending ?? [], historyStorage?.history ?? []);
dialog = copy(dialog);
let removeUnread = 0;
for(let mid of history) {
let message = this.getMessage(mid);
if(/* message._ != 'messageEmpty' && */message.id > 0) {
messages.push(message);
if(message.fromID != dialog.peerID) {
peers[message.fromID] = appPeersManager.getPeer(message.fromID);
}
dialog.top_message = message.mid;
if(message.fromID != dialog.peerID) {
peers[message.fromID] = appPeersManager.getPeer(message.fromID);
break;
} else if(message.pFlags && message.pFlags.unread) {
++removeUnread;
}
}
if(removeUnread && dialog.unread_count) dialog.unread_count -= removeUnread;
dialogs.push(dialog);
peers[dialog.peerID] = appPeersManager.getPeer(dialog.peerID);
@ -2137,13 +2168,13 @@ export class AppMessagesManager { @@ -2137,13 +2168,13 @@ export class AppMessagesManager {
else message.pFlags.unread = false;
}
if(this.historiesStorage[peerID] === undefined && !message.deleted) {
var historyStorage: any = {count: null, history: [], pending: []};
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid)
if(this.historiesStorage[peerID] === undefined/* && !message.deleted */) { // warning
let historyStorage: HistoryStorage = {count: null, history: [], pending: []};
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid);
if(mid < 0 && message.pFlags.unread) {
dialog.unread_count++;
}
this.historiesStorage[peerID] = historyStorage
this.historiesStorage[peerID] = historyStorage;
if(this.mergeReplyKeyboard(historyStorage, message)) {
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
}
@ -2154,7 +2185,7 @@ export class AppMessagesManager { @@ -2154,7 +2185,7 @@ export class AppMessagesManager {
}
}
public mergeReplyKeyboard(historyStorage: any, message: any) {
public mergeReplyKeyboard(historyStorage: HistoryStorage, message: any) {
// console.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup)
if(!message.reply_markup &&
!message.pFlags.out &&
@ -3204,6 +3235,7 @@ export class AppMessagesManager { @@ -3204,6 +3235,7 @@ export class AppMessagesManager {
}
case 'updateServiceNotification': {
console.log('updateServiceNotification', update);
var fromID = 777000;
var peerID = fromID;
var messageID = this.tempID--;

41
src/lib/appManagers/appSidebarLeft.ts

@ -387,6 +387,9 @@ class AppEditProfileTab implements SliderTab { @@ -387,6 +387,9 @@ class AppEditProfileTab implements SliderTab {
private avatarElem = document.createElement('avatar-element');
private profileUrlContainer = this.container.querySelector('.profile-url-container') as HTMLDivElement;
private profileUrlAnchor = this.profileUrlContainer.lastElementChild as HTMLAnchorElement;
private originalValues = {
firstName: '',
lastName: '',
@ -411,21 +414,27 @@ class AppEditProfileTab implements SliderTab { @@ -411,21 +414,27 @@ class AppEditProfileTab implements SliderTab {
this.lastNameInput.addEventListener('input', () => this.handleChange());
this.bioInput.addEventListener('input', () => this.handleChange());
this.userNameInput.addEventListener('input', () => {
this.handleChange();
let value = this.userNameInput.value;
console.log('userNameInput:', value);
if(value == this.originalValues.userName) {
if(value == this.originalValues.userName || !value.length) {
this.userNameInput.classList.remove('valid', 'error');
userNameLabel.innerText = 'Username (optional)';
this.setProfileUrl();
this.handleChange();
return;
} else if(value.length < 5 || value.length > 32 || !/^[a-zA-Z0-9_]+$/.test(value)) { // does not check the last underscore
} else if(!this.isUsernameValid(value)) { // does not check the last underscore
this.userNameInput.classList.add('error');
this.userNameInput.classList.remove('valid');
userNameLabel.innerText = 'Username is invalid';
} else {
this.userNameInput.classList.remove('error');
/* */
this.userNameInput.classList.remove('valid', 'error');
}
if(this.userNameInput.classList.contains('error')) {
this.setProfileUrl();
this.handleChange();
return;
}
apiManager.invokeApi('account.checkUsername', {
@ -453,6 +462,9 @@ class AppEditProfileTab implements SliderTab { @@ -453,6 +462,9 @@ class AppEditProfileTab implements SliderTab {
break;
}
}
}).then(() => {
this.handleChange();
this.setProfileUrl();
});
});
@ -509,16 +521,33 @@ class AppEditProfileTab implements SliderTab { @@ -509,16 +521,33 @@ class AppEditProfileTab implements SliderTab {
}
this.uploadAvatar = null;
this.setProfileUrl();
}
public isUsernameValid(username: string) {
return ((username.length >= 5 && username.length <= 32) || !username.length) && /^[a-zA-Z0-9_]*$/.test(username);
}
private isChanged() {
return !!this.uploadAvatar
|| this.firstNameInput.value != this.originalValues.firstName
|| this.lastNameInput.value != this.originalValues.lastName
|| this.userNameInput.value != this.originalValues.userName
|| (this.userNameInput.value != this.originalValues.userName && !this.userNameInput.classList.contains('error'))
|| this.bioInput.value != this.originalValues.bio;
}
private setProfileUrl() {
if(this.userNameInput.classList.contains('error') || !this.userNameInput.value.length) {
this.profileUrlContainer.style.display = 'none';
} else {
this.profileUrlContainer.style.display = '';
let url = 'https://t.me/' + this.userNameInput.value;
this.profileUrlAnchor.innerText = url;
this.profileUrlAnchor.href = url;
}
}
private handleChange() {
if(this.isChanged()) {
this.nextBtn.classList.add('is-visible');

130
src/lib/appManagers/appSidebarRight.ts

@ -29,8 +29,6 @@ let setText = (text: string, el: HTMLDivElement) => { @@ -29,8 +29,6 @@ let setText = (text: string, el: HTMLDivElement) => {
el.prepend(p);
el.style.display = '';
//this.scroll.getScrollTopOffset();
});
};
@ -66,7 +64,8 @@ class AppSidebarRight { @@ -66,7 +64,8 @@ class AppSidebarRight {
private loadedAllMedia: {[type: string]: boolean} = {};
public sharedMediaTypes = [
'inputMessagesFilterContacts',
'members',
//'inputMessagesFilterContacts',
'inputMessagesFilterPhotoVideo',
'inputMessagesFilterDocument',
'inputMessagesFilterUrl',
@ -75,7 +74,7 @@ class AppSidebarRight { @@ -75,7 +74,7 @@ class AppSidebarRight {
public sharedMediaType: AppSidebarRight['sharedMediaTypes'][number] = '';
private sharedMediaSelected: HTMLDivElement = null;
private lazyLoadQueueSidebar = new LazyLoadQueue(5);
private lazyLoadQueue = new LazyLoadQueue(5);
public historiesStorage: {
[peerID: number]: {
@ -114,15 +113,14 @@ class AppSidebarRight { @@ -114,15 +113,14 @@ class AppSidebarRight {
let container = this.profileContentEl.querySelector('.content-container .tabs-container') as HTMLDivElement;
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement;
this.scroll = new Scrollable(this.profileContainer, 'y', 1200, 'SR', undefined, 400);
//this.scroll = new Scrollable(this.profileContentEl, 'y', 1200, 'SR', undefined, 400);
this.scroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
this.scroll = new Scrollable(this.profileContainer, 'y', 'SR');
this.scroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && this.sharedMediaSelected.childElementCount/* && false */) {
this.log('onScrolledBottom will load media');
this.loadSidebarMedia(true);
}
};
this.scroll.attachSentinels(undefined, 400);
horizontalMenu(this.profileTabs, container, (id, tabContent) => {
if(this.prevTabID == id) return;
@ -130,24 +128,19 @@ class AppSidebarRight { @@ -130,24 +128,19 @@ class AppSidebarRight {
this.sharedMediaType = this.sharedMediaTypes[id];
this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement;
if(this.profileTabs.offsetTop) {
if(this.prevTabID != -1 && this.profileTabs.offsetTop) {
this.scroll.scrollTop -= this.profileTabs.offsetTop;
}
this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount);
this.scroll.setVirtualContainer(this.sharedMediaSelected);
/* this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount);
this.scroll.setVirtualContainer(this.sharedMediaSelected); */
if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix
this.contentContainer.classList.remove('loaded');
//this.contentContainer.classList.remove('loaded');
this.loadSidebarMedia(true);
}
this.prevTabID = id;
this.scroll.onScroll();
}, () => {
this.onSidebarScroll.bind(this);
this.scroll.onScroll();
});
let sidebarCloseBtn = this.sidebarEl.querySelector('.sidebar-close-button') as HTMLButtonElement;
@ -183,9 +176,7 @@ class AppSidebarRight { @@ -183,9 +176,7 @@ class AppSidebarRight {
//let checked = this.profileElements.notificationsCheckbox.checked;
appImManager.mutePeer(this.peerID);
});
window.addEventListener('resize', this.onSidebarScroll.bind(this));
if(testScroll) {
let div = document.createElement('div');
for(let i = 0; i < 500; ++i) {
@ -209,24 +200,77 @@ class AppSidebarRight { @@ -209,24 +200,77 @@ class AppSidebarRight {
this.searchContainer.classList.add('active');
this.privateSearch.beginSearch(this.peerID);
}
public onSidebarScroll() {
this.lazyLoadQueueSidebar.check();
}
public toggleSidebar(enable?: boolean) {
/////this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl));
let active = this.sidebarEl.classList.contains('active');
let willChange: boolean;
if(enable !== undefined) {
if(enable) {
setTimeout(() => this.lazyLoadQueueSidebar.check(), 200);
this.sidebarEl.classList.add('active');
} else this.sidebarEl.classList.remove('active');
return;
if(!active) {
willChange = true;
}
} else if(active) {
willChange = true;
}
} else {
willChange = true;
}
this.sidebarEl.classList.toggle('active');
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');
}
};
return new Promise((resolve, reject) => {
let hidden: {element: HTMLDivElement, height: number}[] = [];
let observer = new IntersectionObserver((entries) => {
for(let entry of entries) {
let bubble = entry.target as HTMLDivElement;
if(!entry.isIntersecting) {
hidden.push({element: bubble, height: bubble.scrollHeight});
}
}
for(let item of hidden) {
item.element.style.minHeight = item.height + 'px';
(item.element.firstElementChild as HTMLElement).style.display = 'none';
item.element.style.width = '1px';
}
//console.log('hidden', hidden);
observer.disconnect();
set();
setTimeout(() => {
for(let item of hidden) {
item.element.style.minHeight = '';
item.element.style.width = '';
(item.element.firstElementChild as HTMLElement).style.display = '';
}
resolve();
}, 200);
});
let length = Object.keys(appImManager.bubbles).length;
if(length) {
for(let i in appImManager.bubbles) {
observer.observe(appImManager.bubbles[i]);
}
} else {
set();
setTimeout(resolve, 200);
}
});
}
public filterMessagesByType(ids: number[], type: string) {
@ -396,7 +440,7 @@ class AppSidebarRight { @@ -396,7 +440,7 @@ class AppSidebarRight {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, img, false, false);
}
this.lazyLoadQueueSidebar.push({div, load});
this.lazyLoadQueue.push({div, load});
}
this.lastSharedMediaDiv.append(div);
@ -451,10 +495,11 @@ class AppSidebarRight { @@ -451,10 +495,11 @@ class AppSidebarRight {
previewDiv.classList.remove('empty');
previewDiv.innerText = '';
renderImageFromUrl(previewDiv, webpage.photo.url);
});
this.lazyLoadQueueSidebar.push({div: previewDiv, load});
this.lazyLoadQueue.push({div: previewDiv, load});
}
let title = webpage.rTitle || '';
@ -493,8 +538,8 @@ class AppSidebarRight { @@ -493,8 +538,8 @@ class AppSidebarRight {
}
default:
//console.warn('death is my friend', message);
break;
console.warn('death is my friend', messages);
break;
}
if(this.lastSharedMediaDiv.childElementCount && !this.scroll.contains(this.lastSharedMediaDiv)) {
@ -521,11 +566,9 @@ class AppSidebarRight { @@ -521,11 +566,9 @@ class AppSidebarRight {
let parent = sharedMediaDiv.parentElement;
if(parent.lastElementChild.classList.contains('preloader')) {
parent.lastElementChild.remove();
this.contentContainer.classList.add('loaded');
//this.contentContainer.classList.add('loaded');
}
}
this.onSidebarScroll();
}
public loadSidebarMedia(single = false) {
@ -626,11 +669,8 @@ class AppSidebarRight { @@ -626,11 +669,8 @@ class AppSidebarRight {
this.lastSharedMediaDiv.classList.add('media-row');
this.prevTabID = -1;
this.scroll.setVirtualContainer(null);
this.mediaDivsByIDs = {};
this.lazyLoadQueueSidebar.clear();
this.lazyLoadQueue.clear();
this.sharedMediaTypes.forEach(type => {
this.usedFromHistory[type] = 0;
@ -640,9 +680,9 @@ class AppSidebarRight { @@ -640,9 +680,9 @@ class AppSidebarRight {
}
public cleanupHTML() {
this.contentContainer.classList.remove('loaded');
//this.contentContainer.classList.remove('loaded');
this.profileContentEl.parentElement.scrollTop = 0;
//this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
@ -710,7 +750,10 @@ class AppSidebarRight { @@ -710,7 +750,10 @@ class AppSidebarRight {
});
}
let membersLi = this.profileTabs.firstElementChild.children[0] as HTMLLIElement;
if(peerID > 0) {
membersLi.style.display = 'none';
let user = appUsersManager.getUser(peerID);
if(user.phone && peerID != $rootScope.myID) {
setText(user.rPhone, this.profileElements.phone);
@ -734,6 +777,7 @@ class AppSidebarRight { @@ -734,6 +777,7 @@ class AppSidebarRight {
}
});
} else {
membersLi.style.display = appPeersManager.isBroadcast(peerID) ? 'none' : '';
let chat = appPeersManager.getPeer(peerID);
appProfileManager.getChatFull(chat.id).then((chatFull: any) => {

3
src/lib/appManagers/appStickersManager.ts

@ -118,7 +118,8 @@ class AppStickersManager { @@ -118,7 +118,8 @@ class AppStickersManager {
public getAnimatedEmojiSticker(emoji: string) {
let stickerSet = this.stickerSets.emoji;
return stickerSet.documents.find(doc => doc.attributes.find(attribute => attribute.alt == emoji));
emoji = emoji.replace(/\ufe0f/g, '');
return stickerSet.documents.find(doc => doc.stickerEmojiRaw == emoji);
}
public async saveStickerSet(res: {

42
src/lib/config.ts

File diff suppressed because one or more lines are too long

4
src/lib/lottie.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// @ts-ignore
import LottiePlayer from "lottie-web/build/player/lottie_canvas.min.js";
//import LottiePlayer from "lottie-web/build/player/lottie_light.min.js";
//import LottiePlayer from "lottie-web/build/player/lottie_canvas.min.js";
import LottiePlayer from "lottie-web/build/player/lottie_light.min.js";
(window as any).lottie = LottiePlayer;

15
src/lib/lottieLoader.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { isElementInViewport, isInDOM } from "./utils";
import { isInDOM } from "./utils";
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d";
class LottieLoader {
@ -59,7 +59,7 @@ class LottieLoader { @@ -59,7 +59,7 @@ class LottieLoader {
continue;
}
if(canvas) {
/* if(canvas) {
let c = container.firstElementChild as HTMLCanvasElement;
if(!c) {
console.warn('no canvas element for check!', container, animations[i]);
@ -70,11 +70,11 @@ class LottieLoader { @@ -70,11 +70,11 @@ class LottieLoader {
//console.log('lottie need resize');
animation.resize();
}
}
} */
if(!autoplay) continue;
if(blurred || !isElementInViewport(container)) {
/* if(blurred || !isElementInViewport(container)) {
if(!paused) {
this.debug && console.log('pause animation', isElementInViewport(container), container);
animation.pause();
@ -84,7 +84,7 @@ class LottieLoader { @@ -84,7 +84,7 @@ class LottieLoader {
this.debug && console.log('play animation', container);
animation.play();
animations[i].paused = false;
}
} */
}
}
}
@ -92,7 +92,8 @@ class LottieLoader { @@ -92,7 +92,8 @@ class LottieLoader {
public async loadAnimation(params: /* any */AnimationConfigWithPath | AnimationConfigWithData, group = '') {
//params.autoplay = false;
//if(group != 'auth') {
params.renderer = 'canvas';
//params.renderer = 'canvas';
params.renderer = 'svg';
//}
params.rendererSettings = {
@ -131,7 +132,7 @@ class LottieLoader { @@ -131,7 +132,7 @@ class LottieLoader {
container: params.container as HTMLDivElement,
paused: !params.autoplay,
autoplay: params.autoplay,
canvas: params.renderer == 'canvas'
canvas: false//params.renderer == 'canvas'
});
if(params.autoplay) {

9
src/lib/mtproto/apiFileManager.ts

@ -32,8 +32,6 @@ export class ApiFileManager { @@ -32,8 +32,6 @@ export class ApiFileManager {
} = {};
public downloadActives: any = {};
public index = 0;
private log: ReturnType<typeof logger> = logger('AFM');
public downloadRequest(dcID: string | number, cb: () => Promise<unknown>, activeDelta?: number) {
@ -45,9 +43,8 @@ export class ApiFileManager { @@ -45,9 +43,8 @@ export class ApiFileManager {
var downloadPull = this.downloadPulls[dcID];
let promise = new Promise((resolve, reject) => {
// WARNING deferred!
downloadPull.push({cb: cb, deferred: {resolve, reject}, activeDelta: activeDelta});
}).catch(() => {});
})/* .catch(() => {}) */;
setTimeout(() => {
this.downloadCheck(dcID);
@ -69,8 +66,6 @@ export class ApiFileManager { @@ -69,8 +66,6 @@ export class ApiFileManager {
this.downloadActives[dcID] += activeDelta;
this.index++;
data.cb()
.then((result: any) => {
this.downloadActives[dcID] -= activeDelta;
@ -415,7 +410,7 @@ export class ApiFileManager { @@ -415,7 +410,7 @@ export class ApiFileManager {
});
});
});
});
}, errorHandler);
})(offset + limit >= size, offset, writeFileDeferred, writeFilePromise);
writeFilePromise = writeFileDeferred;

123
src/lib/richtextprocessor.js

File diff suppressed because one or more lines are too long

117
src/lib/utils.js

@ -441,42 +441,6 @@ export function findUpTag(el, tag) { @@ -441,42 +441,6 @@ export function findUpTag(el, tag) {
return null;
}
export function isElementInViewport(el) {
var rect = el.getBoundingClientRect(),
vWidth = window.innerWidth || document.documentElement.clientWidth,
vHeight = window.innerHeight || document.documentElement.clientHeight,
efp = function(x, y) { return document.elementFromPoint(x, y) };
// Return false if it's not in the viewport
if(rect.right < 0 || rect.bottom < 0
|| rect.left > vWidth || rect.top > vHeight
|| !rect.width || !rect.height) {
return false;
}
let elements = [
efp(rect.left + 1, rect.top + 1),
efp(rect.right - 1, rect.top + 1),
efp(rect.right - 1, rect.bottom - 1),
efp(rect.left + 1, rect.bottom - 1)
];
// Return true if any of its four corners are visible
return elements.find(e => el.contains(e) || el.parentElement == e) !== undefined;
}
export function isScrolledIntoView(el) {
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
// Only completely visible elements return true:
//var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
// Partially visible elements return true:
var isVisible = elemTop < window.innerHeight && elemBottom >= 0;
return isVisible;
}
export function whichChild(elem/* : Node */) {
let i = 0;
// @ts-ignore
@ -645,11 +609,11 @@ export function calcImageInBox (imageW, imageH, boxW, boxH, noZooom) { @@ -645,11 +609,11 @@ export function calcImageInBox (imageW, imageH, boxW, boxH, noZooom) {
* @param {String} input The emoji character.
* @returns {String} The base 16 unicode code.
*/
/* export function emojiUnicode (input) {
let pairs = emojiUnicode.raw(input).split(' ').map(val => parseInt(val).toString(16));
if(pairs[0].length == 2) pairs[0] = '00' + pairs[0];
return pairs.join('-').toUpperCase();
} */
export function emojiUnicode(input) {
let pairs = emojiUnicode.raw(input).split(' ').map(val => parseInt(val).toString(16)).filter(p => p != 'fe0f');
if(pairs.length && pairs[0].length == 2) pairs[0] = '00' + pairs[0];
return pairs.join('-');
}
/**
* emojiunicode.raw
@ -660,7 +624,7 @@ export function calcImageInBox (imageW, imageH, boxW, boxH, noZooom) { @@ -660,7 +624,7 @@ export function calcImageInBox (imageW, imageH, boxW, boxH, noZooom) {
* @param {String} input The emoji character.
* @returns {String} The unicode code points.
*/
/* emojiUnicode.raw = function (input) {
emojiUnicode.raw = function(input) {
if(input.length === 1) {
return input.charCodeAt(0).toString();
} else if(input.length > 1) {
@ -685,74 +649,7 @@ export function calcImageInBox (imageW, imageH, boxW, boxH, noZooom) { @@ -685,74 +649,7 @@ export function calcImageInBox (imageW, imageH, boxW, boxH, noZooom) {
}
return '';
}; */
// country code regex
const CC_REGEX = /^[a-z]{2}$/i;
// offset between uppercase ascii and regional indicator symbols
const OFFSET = 127397;
/**
* convert country code to corresponding emoji flag
* @param {string} cc - country code string
* @returns {string} country code emoji
*/
export function countryCodeEmoji(cc/* : string */) {
if(!CC_REGEX.test(cc)) {
const type = typeof cc;
throw new TypeError(
`cc argument must be an ISO 3166-1 alpha-2 string, but got '${
type === 'string' ? cc : type
}' instead.`,
);
}
const chars = [...cc.toUpperCase()].map(c => c.charCodeAt(0) + OFFSET);
//console.log(chars);
return String.fromCodePoint(...chars);
}
export function unifiedCountryCodeEmoji(cc/* : string */) {
if(!CC_REGEX.test(cc)) {
const type = typeof cc;
throw new TypeError(
`cc argument must be an ISO 3166-1 alpha-2 string, but got '${
type === 'string' ? cc : type
}' instead.`,
);
}
const chars = [...cc.toUpperCase()].map(c => c.charCodeAt(0) + OFFSET);
return chars.map(c => c.toString(16).toUpperCase()).join('-');
}
function versionCompare (ver1, ver2) {
if (typeof ver1 !== 'string') {
ver1 = ''
}
if (typeof ver2 !== 'string') {
ver2 = ''
}
ver1 = ver1.replace(/^\s+|\s+$/g, '').split('.')
ver2 = ver2.replace(/^\s+|\s+$/g, '').split('.')
var a = Math.max(ver1.length, ver2.length), i
for (i = 0; i < a; i++) {
if (ver1[i] == ver2[i]) {
continue
}
if (ver1[i] > ver2[i]) {
return 1
} else {
return -1
}
}
return 0
}
};
//var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g,
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g,

26
src/scss/partials/_chatBubble.scss

@ -106,7 +106,7 @@ @@ -106,7 +106,7 @@
}
}
&.forwarded {
/* &.forwarded {
.forward {
opacity: 0;
position: absolute;
@ -129,6 +129,24 @@ @@ -129,6 +129,24 @@
height: 20px;
}
}
} */
.goto-original {
opacity: 0;
position: absolute;
right: -46px;
bottom: 0;
width: 38px;
height: 38px;
font-size: 1.5rem;
align-items: center;
display: flex;
justify-content: center;
color: #fff;
border-radius: 50%;
background: rgba(0, 0, 0, 0.16);
cursor: pointer;
transition: .2s opacity;
}
/* &.is-group-first {
@ -161,10 +179,8 @@ @@ -161,10 +179,8 @@
}
}
&:hover {
.forward {
opacity: 1;
}
.goto-original {
opacity: 1;
}
.reply {

4
src/scss/partials/_rightSIdebar.scss

@ -75,12 +75,12 @@ @@ -75,12 +75,12 @@
position: relative;
//height: 1%; // fix safari
&.loaded {
/* &.loaded { // warning
.profile-tabs-content {
position: relative;
min-height: auto;
}
}
} */
}
}

8
src/scss/partials/_scrollable.scss

@ -50,6 +50,14 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -50,6 +50,14 @@ div.scrollable::-webkit-scrollbar-thumb {
-ms-overflow-style: none;
}
&-sentinel {
position: relative;
left: 0;
height: 1px;
background-color: transparent;
width: 1px;
}
/* &.scrollable-x ~ .scrollbar-thumb {
top: auto;
right: auto;

Loading…
Cancel
Save