Browse Source

sticker thumbs & safari open chat fix & online blink fix & login added idle monkey & webp safari save to indexeddb & lazyload queue reorder & minor fixes

master
morethanwords 4 years ago
parent
commit
22ceba971d
  1. 4
      src/components/appForward.ts
  2. 16
      src/components/appSelectPeers.ts
  3. 4
      src/components/chatInput.ts
  4. 25
      src/components/emoticonsDropdown.ts
  5. 14
      src/components/lazyLoadQueue.ts
  6. 25
      src/components/misc.ts
  7. 109
      src/components/scrollable_new.ts
  8. 237
      src/components/wrappers.ts
  9. 65
      src/lib/appManagers/appChatsManager.ts
  10. 6
      src/lib/appManagers/appDialogsManager.ts
  11. 106
      src/lib/appManagers/appDocsManager.ts
  12. 145
      src/lib/appManagers/appImManager.ts
  13. 21
      src/lib/appManagers/appMediaViewer.ts
  14. 8
      src/lib/appManagers/appMessagesManager.ts
  15. 50
      src/lib/appManagers/appPhotosManager.ts
  16. 4
      src/lib/appManagers/appSidebarRight.ts
  17. 55
      src/lib/appManagers/appStickersManager.ts
  18. 94
      src/lib/appManagers/appWebpManager.ts
  19. 18
      src/lib/bin_utils.ts
  20. 12
      src/lib/filemanager.ts
  21. 2
      src/lib/idb.ts
  22. 8
      src/lib/lottieLoader.ts
  23. 122
      src/lib/mtproto/apiFileManager.ts
  24. 9
      src/lib/mtproto/networker.ts
  25. 18
      src/lib/mtproto/networkerFactory.ts
  26. 14
      src/lib/mtproto/tl_utils.ts
  27. 6
      src/lib/utils.js
  28. 44
      src/lib/webp.ts
  29. 75
      src/pages/pageAuthCode.ts
  30. 2
      src/pages/pageSignIn.ts
  31. 1
      src/scss/partials/_chat.scss
  32. 16
      src/scss/partials/_chatBubble.scss
  33. 9
      src/scss/partials/_emojiDropdown.scss
  34. 17
      src/scss/partials/_rightSIdebar.scss
  35. 8
      src/scss/partials/_scrollable.scss
  36. 1
      src/scss/partials/popups/_mediaAttacher.scss
  37. 34
      src/scss/style.scss

4
src/components/appForward.ts

@ -56,7 +56,6 @@ class AppForward { @@ -56,7 +56,6 @@ class AppForward {
this.cleanup();
this.msgIDs = ids;
appSidebarRight.toggleSidebar(true);
this.container.classList.add('active');
this.sendBtn.innerHTML = '';
this.sendBtn.classList.add('tgico-send');
@ -68,6 +67,9 @@ class AppForward { @@ -68,6 +67,9 @@ class AppForward {
} else {
this.sendBtn.classList.remove('is-visible');
}
}, 'dialogs', () => {
console.log('forward rendered:', this.container.querySelector('.selector ul').childElementCount);
appSidebarRight.toggleSidebar(true);
});
}
}

16
src/components/appSelectPeers.ts

@ -29,7 +29,7 @@ export class AppSelectPeers { @@ -29,7 +29,7 @@ export class AppSelectPeers {
private query = '';
private cachedContacts: number[];
constructor(private appendTo: HTMLDivElement, private onChange?: (length: number) => void, private peerType: 'contacts' | 'dialogs' = 'dialogs') {
constructor(private appendTo: HTMLDivElement, private onChange?: (length: number) => void, private peerType: 'contacts' | 'dialogs' = 'dialogs', onFirstRender?: () => void) {
this.container.classList.add('selector');
let topContainer = document.createElement('div');
@ -107,12 +107,18 @@ export class AppSelectPeers { @@ -107,12 +107,18 @@ export class AppSelectPeers {
this.container.append(topContainer, delimiter, this.chatsContainer);
appendTo.append(this.container);
this.getMoreResults();
let getResultsPromise = this.getMoreResults() as Promise<any>;
if(onFirstRender) {
getResultsPromise.then(() => {
onFirstRender();
});
}
}
private getMoreDialogs() {
// в десктопе - сначала без группы, потом архивные, потом контакты без сообщений
appMessagesManager.getConversations(this.offsetIndex, 50, 0).then(value => {
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
return appMessagesManager.getConversations(this.offsetIndex, pageCount, 0).then(value => {
let dialogs = value.dialogs;
let newOffsetIndex = dialogs[value.dialogs.length - 1].index || 0;
@ -150,9 +156,9 @@ export class AppSelectPeers { @@ -150,9 +156,9 @@ export class AppSelectPeers {
private getMoreResults() {
if(this.peerType == 'dialogs') {
this.getMoreDialogs();
return this.getMoreDialogs();
} else {
this.getMoreContacts();
return this.getMoreContacts();
}
}

4
src/components/chatInput.ts

@ -13,7 +13,7 @@ import lottieLoader from "../lib/lottieLoader"; @@ -13,7 +13,7 @@ import lottieLoader from "../lib/lottieLoader";
import { Layouter, RectPart } from "./groupedLayout";
export class ChatInput {
public pageEl = document.querySelector('.page-chats') as HTMLDivElement;
public pageEl = document.getElementById('page-chats') as HTMLDivElement;
public messageInput = document.getElementById('input-message') as HTMLDivElement/* HTMLInputElement */;
public fileInput = document.getElementById('input-file') as HTMLInputElement;
public inputMessageContainer = document.getElementsByClassName('input-message-container')[0] as HTMLDivElement;
@ -463,6 +463,7 @@ export class ChatInput { @@ -463,6 +463,7 @@ export class ChatInput {
this.emoticonsDropdown.classList.remove('active');
this.toggleEmoticons.classList.remove('active');
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
this.emoticonsLazyLoadQueue.lock();
}, 200);
};
@ -471,6 +472,7 @@ export class ChatInput { @@ -471,6 +472,7 @@ export class ChatInput {
};
} else {
this.emoticonsDropdown.classList.add('active');
this.emoticonsLazyLoadQueue.unlock();
}
this.toggleEmoticons.classList.add('active');

25
src/components/emoticonsDropdown.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { AppImManager } from "../lib/appManagers/appImManager";
import { AppMessagesManager } from "../lib/appManagers/appMessagesManager";
import { horizontalMenu } from "./misc";
import { horizontalMenu, renderImageFromUrl } from "./misc";
import lottieLoader from "../lib/lottieLoader";
//import Scrollable from "./scrollable";
import Scrollable from "./scrollable_new";
@ -12,7 +12,6 @@ import apiManager from '../lib/mtproto/mtprotoworker'; @@ -12,7 +12,6 @@ import apiManager from '../lib/mtproto/mtprotoworker';
//import CryptoWorker from '../lib/crypto/cryptoworker';
import LazyLoadQueue from "./lazyLoadQueue";
import { MTDocument, wrapSticker } from "./wrappers";
import appWebpManager from "../lib/appManagers/appWebpManager";
import appDocsManager from "../lib/appManagers/appDocsManager";
import ProgressivePreloader from "./preloader";
import Config from "../lib/config";
@ -29,15 +28,18 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -29,15 +28,18 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let container = pageEl.querySelector('.emoji-container .tabs-container') as HTMLDivElement;
let tabs = pageEl.querySelector('.emoji-dropdown .emoji-tabs') as HTMLUListElement;
let tabID = -1;
horizontalMenu(tabs, container, (id) => {
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
if(id == 1 && stickersInit) {
tabID = id;
}, () => {
if(tabID == 1 && stickersInit) {
stickersInit();
} else if(id == 2 && gifsInit) {
} else if(tabID == 2 && gifsInit) {
gifsInit();
}
}, () => {
lottieLoader.checkAnimations(false, EMOTICONSSTICKERGROUP);
});
@ -347,15 +349,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -347,15 +349,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
stickersInit = null;
Promise.all([
apiManager.invokeApi('messages.getRecentStickers', {flags: 0, hash: 0}).then((res) => {
let stickers: {
_: string,
hash: number,
packs: any[],
stickers: MTDocument[],
dates: number[]
} = res as any;
appStickersManager.getRecentStickers().then(stickers => {
let categoryDiv = document.createElement('div');
categoryDiv.classList.add('sticker-category');
@ -408,8 +402,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -408,8 +402,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
reader.readAsArrayBuffer(blob);
} else {
let image = new Image();
//image.src = URL.createObjectURL(blob);
appWebpManager.polyfillImage(image, blob);
renderImageFromUrl(image, URL.createObjectURL(blob));
li.append(image);
}

14
src/components/lazyLoadQueue.ts

@ -19,16 +19,18 @@ export default class LazyLoadQueue { @@ -19,16 +19,18 @@ export default class LazyLoadQueue {
constructor(private parallelLimit = 5) {
this.observer = new IntersectionObserver(entries => {
if(this.lockPromise) return;
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;
}
// need for set element first if scrolled
let item = this.lazyLoadMedia.findAndSplice(i => i.div == target);
if(item) {
item.wasSeen = true;
this.lazyLoadMedia.unshift(item);
this.processQueue(item);
}
}
}

25
src/components/misc.ts

@ -121,26 +121,29 @@ let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceEl @@ -121,26 +121,29 @@ let set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceEl
else elem.style.backgroundImage = 'url(' + url + ')';
};
export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceElement, url: string) {
export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLSourceElement, url: string): Promise<boolean> {
if(loadedURLs[url]) {
set(elem, url);
return true;
return Promise.resolve(true);
}
if(elem instanceof HTMLSourceElement) {
elem.src = url;
return Promise.resolve(false);
} else {
let loader = new Image();
loader.src = url;
//let perf = performance.now();
loader.addEventListener('load', () => {
set(elem, url);
loadedURLs[url] = true;
//console.log('onload:', url, performance.now() - perf);
return new Promise((resolve, reject) => {
let loader = new Image();
loader.src = url;
//let perf = performance.now();
loader.addEventListener('load', () => {
set(elem, url);
loadedURLs[url] = true;
//console.log('onload:', url, performance.now() - perf);
resolve(false);
});
loader.addEventListener('error', reject);
});
}
return false;
}
export function putPreloader(elem: Element, returnDiv = false) {

109
src/components/scrollable_new.ts

@ -56,6 +56,9 @@ export default class Scrollable { @@ -56,6 +56,9 @@ export default class Scrollable {
private lastBottomID = 0;
private lastScrollDirection = 0; // true = bottom
private onScrolledTopFired = false;
private onScrolledBottomFired = false;
public scrollLocked = 0;
private setVisible(element: HTMLElement) {
@ -184,37 +187,56 @@ export default class Scrollable { @@ -184,37 +187,56 @@ 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 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);
// //let fire: () => void;
// this.sentinelsObserver = new IntersectionObserver(entries => {
// for(let entry of entries) {
// let top = entry.target == this.topSentinel;
// if(top) {
// this.onScrolledTopFired = entry.isIntersecting;
// } else {
// this.onScrolledBottomFired = entry.isIntersecting;
// }
// }
// /* this.debug && */this.log('Set onScrolledFires:', this.onScrolledTopFired, this.onScrolledBottomFired);
// /* if((this.onScrolledTopFired || this.onScrolledBottomFired) && !fire) {
// fire = () => window.requestAnimationFrame(() => {
// if(!this.scrollLocked) {
// if(this.onScrolledTopFired && this.onScrolledTop) this.onScrolledTop();
// if(this.onScrolledBottomFired && this.onScrolledBottom) this.onScrolledBottom();
// }
// if(!this.onScrolledTopFired && !this.onScrolledBottomFired) {
// fire = undefined;
// } else {
// fire();
// }
// });
// fire();
// } */
// });
// 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;
@ -231,7 +253,7 @@ export default class Scrollable { @@ -231,7 +253,7 @@ export default class Scrollable {
/* if(this.debug) {
this.log('onScroll call', this.onScrollMeasure);
} */
let appendTo = this.splitUp || this.appendTo;
clearTimeout(this.disableHoverTimeout);
@ -246,8 +268,13 @@ export default class Scrollable { @@ -246,8 +268,13 @@ export default class Scrollable {
this.lastScrollDirection = 0;
}, 100);
if(!this.splitUp || this.onScrollMeasure) return;
if(this.onScrollMeasure) return;
this.onScrollMeasure = window.requestAnimationFrame(() => {
this.checkForTriggers();
this.onScrollMeasure = 0;
if(!this.splitUp) return;
let scrollTop = this.container.scrollTop;
if(this.lastScrollTop != scrollTop) {
this.lastScrollDirection = this.lastScrollTop < scrollTop ? 1 : -1;
@ -255,10 +282,24 @@ export default class Scrollable { @@ -255,10 +282,24 @@ export default class Scrollable {
} else {
this.lastScrollDirection = 0;
}
this.onScrollMeasure = 0;
});
}
public checkForTriggers() {
if(this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) return;
let scrollTop = this.container.scrollTop;
let maxScrollTop = this.container.scrollHeight - this.container.clientHeight;
if(this.onScrolledTop && scrollTop <= this.onScrollOffset) {
this.onScrolledTop();
}
if(this.onScrolledBottom && (maxScrollTop - scrollTop) <= this.onScrollOffset) {
this.onScrolledBottom();
}
}
public reorder() {
(Array.from(this.splitUp.children) as HTMLElement[]).forEach((el, idx) => {
el.dataset.virtual = '' + idx;

237
src/components/wrappers.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import appPhotosManager, { MTPhoto } from '../lib/appManagers/appPhotosManager';
import appPhotosManager from '../lib/appManagers/appPhotosManager';
//import CryptoWorker from '../lib/crypto/cryptoworker';
import apiManager from '../lib/mtproto/mtprotoworker';
import LottieLoader from '../lib/lottieLoader';
@ -8,7 +8,6 @@ import { formatBytes } from "../lib/utils"; @@ -8,7 +8,6 @@ import { formatBytes } from "../lib/utils";
import ProgressivePreloader from './preloader';
import LazyLoadQueue from './lazyLoadQueue';
import apiFileManager from '../lib/mtproto/apiFileManager';
import appWebpManager from '../lib/appManagers/appWebpManager';
import VideoPlayer, { MediaProgressLine } from '../lib/mediaPlayer';
import { RichTextProcessor } from '../lib/richtextprocessor';
import { CancellablePromise } from '../lib/polyfill';
@ -16,6 +15,7 @@ import { renderImageFromUrl } from './misc'; @@ -16,6 +15,7 @@ import { renderImageFromUrl } from './misc';
import appMessagesManager from '../lib/appManagers/appMessagesManager';
import { Layouter, RectPart } from './groupedLayout';
import PollElement from './poll';
import appWebpManager from '../lib/appManagers/appWebpManager';
export type MTDocument = {
_: 'document' | 'documentEmpty',
@ -40,15 +40,15 @@ export type MTDocument = { @@ -40,15 +40,15 @@ export type MTDocument = {
duration?: number,
downloaded?: boolean,
url?: string,
version?: any,
audioTitle?: string,
audioPerformer?: string,
sticker?: boolean,
sticker?: number,
stickerEmoji?: string,
stickerEmojiRaw?: string,
stickerSetInput?: any,
stickerThumbConverted?: true,
animated?: boolean
};
@ -74,49 +74,40 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -74,49 +74,40 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
middleware: () => boolean,
lazyLoadQueue: LazyLoadQueue
}) {
let img: HTMLImageElement | SVGImageElement;
let img: HTMLImageElement;
if(withTail) {
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
} else if(!boxWidth && !boxHeight) { // album
let sizes = doc.thumbs;
if(!doc.downloaded && sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, container, false);
}
img = container.firstElementChild as HTMLImageElement || new Image();
if(!container.contains(img)) {
container.append(img);
}
} else {
if(!container.firstElementChild || (container.firstElementChild.tagName != 'IMG' && container.firstElementChild.tagName != 'VIDEO')) {
let size = appPhotosManager.setAttachmentSize(doc, container, boxWidth, boxHeight);
if(!boxWidth && !boxHeight) { // album
let sizes = doc.thumbs;
if(!doc.downloaded && sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, container, false);
}
} else {
if(!container.firstElementChild || (container.firstElementChild.tagName != 'IMG' && container.firstElementChild.tagName != 'VIDEO')) {
appPhotosManager.setAttachmentSize(doc, container, boxWidth, boxHeight);
}
}
img = container.firstElementChild as HTMLImageElement || new Image();
if(!container.contains(img)) {
container.append(img);
img = container.lastElementChild as HTMLImageElement;
if(!img || img.tagName != 'IMG') {
container.append(img = new Image());
}
}
}
let video = document.createElement('video');
let source = document.createElement('source');
video.append(source);
if(withTail) {
let foreignObject = document.createElementNS("http://www.w3.org/2000/svg", 'foreignObject');
let width = img.getAttributeNS(null, 'width');
let height = img.getAttributeNS(null, 'height');
foreignObject.setAttributeNS(null, 'width', width);
foreignObject.setAttributeNS(null, 'height', height);
video.width = +width;
video.height = +height;
let foreignObject = img.parentElement;
video.width = +foreignObject.getAttributeNS(null, 'width');
video.height = +foreignObject.getAttributeNS(null, 'height');
foreignObject.append(video);
img.parentElement.append(foreignObject);
} else {
container.append(video);
}
let source = document.createElement('source');
video.append(source);
let span: HTMLSpanElement;
if(doc.type != 'round') {
span = document.createElement('span');
@ -154,12 +145,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -154,12 +145,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
source.type = doc.mime_type;
video.append(source);
if(!withTail) {
if(img && container.contains(img)) {
container.removeChild(img);
}
container.append(video);
if(img && img.parentElement) {
img.remove();
}
if(doc.type == 'gif') {
@ -593,14 +580,16 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem @@ -593,14 +580,16 @@ export function wrapVoiceMessage(doc: MTDocument, withTime = false): HTMLDivElem
function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, container: HTMLDivElement, boxWidth: number, boxHeight: number, isOut: boolean) {
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add('bubble__media-container', isOut ? 'is-out' : 'is-in');
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
svg.append(image);
let size = appPhotosManager.setAttachmentSize(photo._ == 'document' ? photo : photo.id, svg, boxWidth, boxHeight);
let foreignObject = document.createElementNS("http://www.w3.org/2000/svg", 'foreignObject');
let width = +svg.getAttributeNS(null, 'width');
let height = +svg.getAttributeNS(null, 'height');
appPhotosManager.setAttachmentSize(photo._ == 'document' ? photo : photo.id, foreignObject, boxWidth, boxHeight);
let width = +foreignObject.getAttributeNS(null, 'width');
let height = +foreignObject.getAttributeNS(null, 'height');
svg.setAttributeNS(null, 'width', '' + width);
svg.setAttributeNS(null, 'height', '' + height);
let clipID = 'clip' + message.mid;
svg.dataset.clipID = clipID;
@ -626,36 +615,38 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, @@ -626,36 +615,38 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string},
defs.innerHTML = `<clipPath id="${clipID}">${clipPathHTML}</clipPath>`;
svg.prepend(defs);
container.appendChild(svg);
container.style.width = parseInt(container.style.width) - 9 + 'px';
svg.append(defs, foreignObject);
container.append(svg);
let img = foreignObject.firstElementChild as HTMLImageElement;
if(!img) {
foreignObject.append(img = new Image());
}
return image;
return img;
}
export function wrapPhoto(photoID: string, message: any, container: HTMLDivElement, boxWidth = 380, boxHeight = 380, withTail = true, isOut = false, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) {
let photo = appPhotosManager.getPhoto(photoID);
let image: SVGImageElement | HTMLImageElement;
let image: HTMLImageElement;
if(withTail) {
image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
} else if(size) { // album
let sizes = photo.sizes;
if(!photo.downloaded && sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, container, false);
} else {
if(size) { // album
let sizes = photo.sizes;
if(!photo.downloaded && sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, container, false);
}
} else if(boxWidth && boxHeight) { // means webpage's preview
size = appPhotosManager.setAttachmentSize(photoID, container, boxWidth, boxHeight, false);
}
image = container.firstElementChild as HTMLImageElement || new Image();
if(!container.contains(image)) {
container.appendChild(image);
}
} else if(boxWidth && boxHeight) { // means webpage's preview
size = appPhotosManager.setAttachmentSize(photoID, container, boxWidth, boxHeight, false);
image = container.firstElementChild as HTMLImageElement || new Image();
if(!container.contains(image)) {
container.appendChild(image);
image = container.lastElementChild as HTMLImageElement;
if(!image || image.tagName != 'IMG') {
container.append(image = new Image());
}
}
@ -694,7 +685,7 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme @@ -694,7 +685,7 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme
}
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false, onlyThumb = false) {
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
let stickerType = doc.sticker;
if(stickerType == 2 && !LottieLoader.loaded) {
LottieLoader.loadLottie();
@ -702,8 +693,10 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -702,8 +693,10 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
if(!stickerType) {
console.error('wrong doc for wrapSticker!', doc);
return Promise.resolve();
throw new Error('wrong doc for wrapSticker!');
}
div.dataset.docID = doc.id;
//console.log('wrap sticker', doc, div, onlyThumb);
@ -713,32 +706,61 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -713,32 +706,61 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
//console.log('wrap sticker', thumb, div);
if(thumb.bytes) {
apiFileManager.saveSmallFile(thumb.location, thumb.bytes);
let img = new Image();
if(appWebpManager.isSupported() || doc.stickerThumbConverted) {
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true));
div.append(img);
} else {
appWebpManager.convertToPng(thumb.bytes).then(bytes => {
if(middleware && !middleware()) return;
thumb.bytes = bytes;
doc.stickerThumbConverted = true;
if(!div.childElementCount) {
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true)).then(() => {
div.append(img);
});
}
});
}
appPhotosManager.setAttachmentPreview(thumb.bytes, div, true);
if(onlyThumb) {
return Promise.resolve();
}
} else if(!onlyThumb && stickerType == 2) {
let img = new Image();
let load = () => appDocsManager.downloadDocThumb(doc, thumb.type).then(url => {
if(!img.parentElement || (middleware && !middleware())) return;
let promise = renderImageFromUrl(img, url);
if(!downloaded) {
promise.then(() => {
div.append(img);
});
}
});
if(onlyThumb) return Promise.resolve();
let downloaded = appDocsManager.hasDownloadedThumb(doc.id, thumb.type);
if(downloaded) {
div.append(img);
}
lazyLoadQueue && !downloaded ? lazyLoadQueue.push({div, load}) : load();
}
}
if(onlyThumb && doc.thumbs) {
if(onlyThumb && doc.thumbs) { // for sticker panel
let thumb = doc.thumbs[0];
let load = () => apiFileManager.downloadSmallFile({
_: 'inputDocumentFileLocation',
access_hash: doc.access_hash,
file_reference: doc.file_reference,
thumb_size: thumb.type,
id: doc.id
}, {dcID: doc.dc_id}).then(blob => {
let load = () => appDocsManager.downloadDocThumb(doc, thumb.type).then(url => {
let img = new Image();
appWebpManager.polyfillImage(img, blob);
div.append(img);
div.dataset.docID = doc.id;
appStickersManager.saveSticker(doc);
renderImageFromUrl(img, url).then(() => {
if(middleware && !middleware()) return;
div.append(img);
});
});
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
@ -748,10 +770,8 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -748,10 +770,8 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
let load = () => appDocsManager.downloadDoc(doc.id).then(blob => {
//console.log('loaded sticker:', blob, div);
if(middleware && !middleware()) return;
/* if(div.firstElementChild) {
div.firstElementChild.remove();
} */
//return;
if(stickerType == 2) {
const reader = new FileReader();
@ -802,15 +822,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -802,15 +822,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
}, {once: true});
}
});
} /* else {
let canvas = div.firstElementChild as HTMLCanvasElement;
if(!canvas.width && !canvas.height) {
console.log('Need lottie resize');
// @ts-ignore
animation.resize();
}
} */
}
if(play) {
animation.play();
@ -831,23 +843,14 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -831,23 +843,14 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
});
}
if(!doc.url) {
appWebpManager.polyfillImage(img, blob).then((url) => {
doc.url = url;
if(div.firstElementChild && div.firstElementChild != img) {
div.firstElementChild.remove();
}
});
} else {
img.src = doc.url;
}
renderImageFromUrl(img, doc.url).then(() => {
if(div.firstElementChild && div.firstElementChild != img) {
div.firstElementChild.remove();
}
div.append(img);
div.append(img);
});
}
div.dataset.docID = doc.id;
appStickersManager.saveSticker(doc);
});
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat'}), Promise.resolve()) : load();

65
src/lib/appManagers/appChatsManager.ts

@ -3,6 +3,7 @@ import { RichTextProcessor } from "../richtextprocessor"; @@ -3,6 +3,7 @@ import { RichTextProcessor } from "../richtextprocessor";
import appUsersManager from "./appUsersManager";
import apiManager from '../mtproto/mtprotoworker';
import apiUpdatesManager from "./apiUpdatesManager";
import appProfileManager from "./appProfileManager";
type Channel = {
_: 'channel',
@ -42,6 +43,8 @@ export class AppChatsManager { @@ -42,6 +43,8 @@ export class AppChatsManager {
public megagroups: any = {};
public cachedPhotoLocations: any = {};
public megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}} = {};
constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
// console.log('on apiUpdate', update)
@ -130,7 +133,7 @@ export class AppChatsManager { @@ -130,7 +133,7 @@ export class AppChatsManager {
return false;
}
var chat = this.getChat(id);
let chat = this.getChat(id);
if(chat._ == 'chatForbidden' ||
chat._ == 'channelForbidden' ||
chat.pFlags.kicked ||
@ -191,7 +194,7 @@ export class AppChatsManager { @@ -191,7 +194,7 @@ export class AppChatsManager {
}
public isChannel(id: number) {
var chat = this.chats[id];
let chat = this.chats[id];
if(chat && (chat._ == 'channel' || chat._ == 'channelForbidden') || this.channelAccess[id]) {
return true;
}
@ -203,7 +206,7 @@ export class AppChatsManager { @@ -203,7 +206,7 @@ export class AppChatsManager {
return true;
}
var chat = this.chats[id];
let chat = this.chats[id];
if(chat && chat._ == 'channel' && chat.pFlags.megagroup) {
return true;
}
@ -246,12 +249,12 @@ export class AppChatsManager { @@ -246,12 +249,12 @@ export class AppChatsManager {
}
public hasChat(id: number, allowMin?: any) {
var chat = this.chats[id]
let chat = this.chats[id]
return isObject(chat) && (allowMin || !chat.pFlags.min);
}
public getChatPhoto(id: number) {
var chat = this.getChat(id);
let chat = this.getChat(id);
if(this.cachedPhotoLocations[id] === undefined) {
this.cachedPhotoLocations[id] = chat && chat.photo ? chat.photo : {empty: true};
@ -261,7 +264,7 @@ export class AppChatsManager { @@ -261,7 +264,7 @@ export class AppChatsManager {
}
public getChatString(id: number) {
var chat = this.getChat(id);
let chat = this.getChat(id);
if(this.isChannel(id)) {
return (this.isMegagroup(id) ? 's' : 'c') + id + '_' + chat.access_hash;
}
@ -277,8 +280,8 @@ export class AppChatsManager { @@ -277,8 +280,8 @@ export class AppChatsManager {
}
public wrapForFull(id: number, fullChat: any) {
var chatFull = copy(fullChat);
var chat = this.getChat(id);
let chatFull = copy(fullChat);
let chat = this.getChat(id);
if(!chatFull.participants_count) {
chatFull.participants_count = chat.participants_count;
@ -300,10 +303,10 @@ export class AppChatsManager { @@ -300,10 +303,10 @@ export class AppChatsManager {
}
public wrapParticipants(id: number, participants: any[]) {
var chat = this.getChat(id);
var myID = appUsersManager.getSelf().id;
let chat = this.getChat(id);
let myID = appUsersManager.getSelf().id;
if(this.isChannel(id)) {
var isAdmin = chat.pFlags.creator || chat.pFlags.editor || chat.pFlags.moderator;
let isAdmin = chat.pFlags.creator || chat.pFlags.editor || chat.pFlags.moderator;
participants.forEach((participant) => {
participant.canLeave = myID == participant.user_id;
participant.canKick = isAdmin && participant._ == 'channelParticipant';
@ -312,7 +315,7 @@ export class AppChatsManager { @@ -312,7 +315,7 @@ export class AppChatsManager {
participant.user = appUsersManager.getUser(participant.user_id);
});
} else {
var isAdmin = chat.pFlags.creator || chat.pFlags.admins_enabled && chat.pFlags.admin;
let isAdmin = chat.pFlags.creator || chat.pFlags.admins_enabled && chat.pFlags.admin;
participants.forEach((participant) => {
participant.canLeave = myID == participant.user_id;
participant.canKick = !participant.canLeave && (
@ -388,6 +391,44 @@ export class AppChatsManager { @@ -388,6 +391,44 @@ export class AppChatsManager {
});
}
}
public async getOnlines(id: number): Promise<number> {
if(this.isMegagroup(id)) {
let timestamp = Date.now() / 1000 | 0;
let cached = this.megagroupOnlines[id] ?? (this.megagroupOnlines[id] = {timestamp: 0, onlines: 1});
if((timestamp - cached.timestamp) < 60) {
return cached.onlines;
}
let res = await apiManager.invokeApi('messages.getOnlines', {
peer: this.getChannelInputPeer(id)
});
let onlines = res.onlines ?? 1;
cached.timestamp = timestamp;
cached.onlines = onlines;
return onlines;
} else if(this.isBroadcast(id)) {
return 1;
}
let chatInfo = appProfileManager.getChatFull(id);
if(chatInfo._ == 'chatFull' && chatInfo.participants && chatInfo.participants.participants) {
let participants = chatInfo.participants.participants;
return participants.reduce((acc: number, participant: any) => {
let user = appUsersManager.getUser(participant.user_id);
if(user && user.status && user.status._ == 'userStatusOnline') {
return acc + 1;
}
return acc;
}, 0);
} else {
return 1;
}
}
}
export default new AppChatsManager();

6
src/lib/appManagers/appDialogsManager.ts

@ -409,12 +409,12 @@ export class AppDialogsManager { @@ -409,12 +409,12 @@ export class AppDialogsManager {
this.scroll = new Scrollable(this.chatsContainer, 'y', 'CL', this.chatList, 500);
this.scroll.setVirtualContainer(this.chatList);
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
this.scroll.attachSentinels();
//this.scroll.attachSentinels();
this.scrollArchived = new Scrollable(this.chatsArchivedContainer, 'y', 'CLA', this.chatListArchived, 500);
this.scrollArchived.setVirtualContainer(this.chatListArchived);
this.scrollArchived.onScrolledBottom = this.onChatsArchivedScroll.bind(this);
this.scroll.attachSentinels();
///this.scroll.attachSentinels();
this.setListClickListener(this.chatList);
this.setListClickListener(this.chatListArchived);
@ -828,7 +828,7 @@ export class AppDialogsManager { @@ -828,7 +828,7 @@ export class AppDialogsManager {
dom.lastTimeSpan.innerHTML = timeStr;
} else dom.lastTimeSpan.innerHTML = '';
if(this.doms[peerID] || this.domsArchived[peerID]) {
if((this.doms[peerID] || this.domsArchived[peerID]) == dom) {
this.setUnreadMessages(dialog);
} else { // means search
dom.listEl.dataset.mid = lastMessage.mid;

106
src/lib/appManagers/appDocsManager.ts

@ -7,8 +7,9 @@ import { isObject } from '../utils'; @@ -7,8 +7,9 @@ import { isObject } from '../utils';
class AppDocsManager {
private docs: {[docID: string]: MTDocument} = {};
private thumbs: {[docIDAndSize: string]: Promise<string>} = {};
public saveDoc(apiDoc: MTDocument/* any */, context?: any) {
public saveDoc(apiDoc: MTDocument, context?: any) {
//console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
if(this.docs[apiDoc.id]) {
let d = this.docs[apiDoc.id];
@ -68,8 +69,6 @@ class AppDocsManager { @@ -68,8 +69,6 @@ class AppDocsManager {
break;
case 'documentAttributeSticker':
apiDoc.sticker = true;
if(attribute.alt !== undefined) {
apiDoc.stickerEmojiRaw = attribute.alt;
apiDoc.stickerEmoji = RichTextProcessor.wrapRichText(apiDoc.stickerEmojiRaw, {noLinks: true, noLinebreaks: true});
@ -83,11 +82,13 @@ class AppDocsManager { @@ -83,11 +82,13 @@ class AppDocsManager {
}
}
if(apiDoc.thumbs && apiDoc.mime_type == 'image/webp') {
if(/* apiDoc.thumbs && */apiDoc.mime_type == 'image/webp') {
apiDoc.type = 'sticker';
apiDoc.sticker = 1;
} else if(apiDoc.mime_type == 'application/x-tgsticker') {
apiDoc.type = 'sticker';
apiDoc.animated = true;
apiDoc.sticker = 2;
}
break;
@ -145,7 +146,7 @@ class AppDocsManager { @@ -145,7 +146,7 @@ class AppDocsManager {
return isObject(docID) ? docID : this.docs[docID];
}
public getInputByID(docID: any) {
public getMediaInputByID(docID: any) {
let doc = this.getDoc(docID);
return {
_: 'inputMediaDocument',
@ -159,6 +160,18 @@ class AppDocsManager { @@ -159,6 +160,18 @@ class AppDocsManager {
ttl_seconds: 0
};
}
public getInputByID(docID: any, thumbSize?: string) {
let doc = this.getDoc(docID);
return {
_: 'inputDocumentFileLocation',
id: doc.id,
access_hash: doc.access_hash,
file_reference: doc.file_reference,
thumb_size: thumbSize
};
}
public getFileName(doc: MTDocument) {
if(doc.file_name) {
@ -173,42 +186,10 @@ class AppDocsManager { @@ -173,42 +186,10 @@ class AppDocsManager {
return 't_' + (doc.type || 'file') + doc.id + fileExt;
}
public updateDocDownloaded(docID: string) {
var doc = this.docs[docID];
var inputFileLocation = {
_: 'inputDocumentFileLocation',
id: docID,
access_hash: doc.access_hash,
version: doc.version,
file_name: this.getFileName(doc)
};
if(doc.downloaded === undefined) {
apiFileManager.getDownloadedFile(inputFileLocation, doc.size).then(() => {
doc.downloaded = true;
}, () => {
doc.downloaded = false;
});
}
}
public downloadDoc(docID: string | MTDocument, toFileEntry?: any): CancellablePromise<Blob> {
let doc: MTDocument;
if(typeof(docID) === 'string') {
doc = this.docs[docID];
} else {
doc = docID;
}
public downloadDoc(docID: any, toFileEntry?: any): CancellablePromise<Blob> {
let doc = this.getDoc(docID);
var inputFileLocation = {
_: 'inputDocumentFileLocation',
id: doc.id,
access_hash: doc.access_hash,
file_reference: doc.file_reference,
thumb_size: '',
version: doc.version,
file_name: this.getFileName(doc)
};
let inputFileLocation = this.getInputByID(doc);
if(doc._ == 'documentEmpty') {
return Promise.reject();
@ -217,26 +198,27 @@ class AppDocsManager { @@ -217,26 +198,27 @@ class AppDocsManager {
if(doc.downloaded && !toFileEntry) {
if(doc.url) return Promise.resolve(null);
var cachedBlob = apiFileManager.getCachedFile(inputFileLocation);
let cachedBlob = apiFileManager.getCachedFile(inputFileLocation);
if(cachedBlob) {
return Promise.resolve(cachedBlob);
}
}
//historyDoc.progress = {enabled: !historyDoc.downloaded, percent: 1, total: doc.size};
// нет смысла делать объект с выполняющимися промисами, нижняя строка и так вернёт загружающийся
var downloadPromise: CancellablePromise<Blob> = apiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, {
let downloadPromise: CancellablePromise<Blob> = apiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, {
mimeType: doc.mime_type || 'application/octet-stream',
toFileEntry: toFileEntry
toFileEntry: toFileEntry,
stickerType: doc.sticker
});
downloadPromise.then((blob) => {
if(blob) {
doc.downloaded = true;
if(/* !doc.animated || */doc.type && doc.type != 'sticker') {
doc.url = FileManager.getFileCorrectUrl(blob, doc.mime_type);
if(doc.type && doc.sticker != 2) {
doc.url = URL.createObjectURL(blob);
}
}
@ -266,6 +248,36 @@ class AppDocsManager { @@ -266,6 +248,36 @@ class AppDocsManager {
return downloadPromise;
}
public downloadDocThumb(docID: any, thumbSize: string) {
let doc = this.getDoc(docID);
let key = doc.id + '-' + thumbSize;
if(this.thumbs[key]) {
return this.thumbs[key];
}
let input = this.getInputByID(doc, thumbSize);
if(doc._ == 'documentEmpty') {
return Promise.reject();
}
let mimeType = doc.sticker ? 'image/webp' : doc.mime_type;
let promise = apiFileManager.downloadSmallFile(input, {
dcID: doc.dc_id,
stickerType: doc.sticker ? 1 : undefined,
mimeType: mimeType
});
return this.thumbs[key] = promise.then((blob) => {
return URL.createObjectURL(blob);
});
}
public hasDownloadedThumb(docID: string, thumbSize: string) {
return !!this.thumbs[docID + '-' + thumbSize];
}
public async saveDocFile(docID: string) {
var doc = this.docs[docID];

145
src/lib/appManagers/appImManager.ts

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
import apiManager from '../mtproto/mtprotoworker';
import { $rootScope, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, findUpTag, langPack, whichChild } from "../utils";
import appUsersManager from "./appUsersManager";
import appMessagesManager, { Dialog } from "./appMessagesManager";
import appMessagesManager from "./appMessagesManager";
import appPeersManager from "./appPeersManager";
import appProfileManager from "./appProfileManager";
import appDialogsManager from "./appDialogsManager";
@ -38,7 +38,7 @@ appSidebarLeft; // just to include @@ -38,7 +38,7 @@ appSidebarLeft; // just to include
let testScroll = false;
export class AppImManager {
public pageEl = document.querySelector('.page-chats') as HTMLDivElement;
public pageEl = document.getElementById('page-chats') as HTMLDivElement;
public btnMute = this.pageEl.querySelector('.tool-mute') as HTMLButtonElement;
public btnMenuMute = this.pageEl.querySelector('.menu-mute') as HTMLButtonElement;
public avatarEl = document.getElementById('im-avatar') as AvatarElement;
@ -754,34 +754,22 @@ export class AppImManager { @@ -754,34 +754,22 @@ export class AppImManager {
this.log('loadMoreHistory', top);
if(!this.peerID || testScroll || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return;
let history = Object.keys(this.bubbles).map(id => +id).sort((a, b) => a - b);
// warning, если иды только отрицательные то вниз не попадёт (хотя мб и так не попадёт)
let history = Object.keys(this.bubbles).map(id => +id).filter(id => id > 0).sort((a, b) => a - b);
if(!history.length) return;
/* let history = appMessagesManager.historiesStorage[this.peerID].history;
let length = history.length; */
// filter negative ids
let lastBadIndex = -1;
for(let i = 0; i < history.length; ++i) {
if(history[i] <= 0) lastBadIndex = i;
else break;
}
if(lastBadIndex != -1) {
history = history.slice(lastBadIndex + 1);
}
if(top && !this.scrolledAll) {
this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history);
/* if(history.length == 75) {
this.log('load more', this.scrollable.scrollHeight, this.scrollable.scrollTop, this.scrollable);
return;
} */
/* false && */this.getHistory(history[0], true);
}
if(this.scrolledAllDown) return;
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
/* if(!dialog) {
this.log.warn('no dialog for load history');
return;
} */
// if scroll down after search
if(!top && (!dialog || history.indexOf(dialog.top_message) === -1)) {
@ -822,14 +810,14 @@ export class AppImManager { @@ -822,14 +810,14 @@ export class AppImManager {
}
public setScroll() {
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner);
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, 300);
this.scroll = this.scrollable.container;
this.bubblesContainer.append(this.goDownBtn);
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
this.scrollable.attachSentinels(undefined, 300);
//this.scrollable.attachSentinels(undefined, 300);
this.scroll.addEventListener('scroll', this.onScroll.bind(this));
this.scroll.parentElement.classList.add('scrolled-down');
@ -850,30 +838,7 @@ export class AppImManager { @@ -850,30 +838,7 @@ export class AppImManager {
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = '';
}
Promise.all([
appPeersManager.isMegagroup(this.peerID) ? apiManager.invokeApi('messages.getOnlines', {
peer: appPeersManager.getInputPeerByID(this.peerID)
}) as Promise<any> : Promise.resolve(),
// will redirect if wrong
appProfileManager.getChatFull(chat.id)
]).then(results => {
let [chatOnlines, chatInfo] = results;
let onlines = chatOnlines ? chatOnlines.onlines : 1;
if(chatInfo._ == 'chatFull' && chatInfo.participants && chatInfo.participants.participants) {
let participants = chatInfo.participants.participants;
onlines = participants.reduce((acc: number, participant: any) => {
let user = appUsersManager.getUser(participant.user_id);
if(user && user.status && user.status._ == 'userStatusOnline') {
return acc + 1;
}
return acc;
}, 0);
}
appProfileManager.getChatFull(chat.id).then((chatInfo: any) => {
this.log('chatInfo res:', chatInfo);
if(chatInfo.pinned_msg_id) { // request pinned message
@ -884,12 +849,17 @@ export class AppImManager { @@ -884,12 +849,17 @@ export class AppImManager {
let participants_count = chatInfo.participants_count || (chatInfo.participants && chatInfo.participants.participants && chatInfo.participants.participants.length);
if(participants_count) {
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
if(onlines > 1) {
subtitle += ', ' + numberWithCommas(onlines) + ' online';
}
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = subtitle;
if(participants_count < 2) return;
appChatsManager.getOnlines(chat.id).then(onlines => {
if(onlines > 1) {
subtitle += ', ' + numberWithCommas(onlines) + ' online';
}
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = subtitle;
});
}
});
} else if(!appUsersManager.isBot(this.peerID)) { // user
@ -1071,7 +1041,7 @@ export class AppImManager { @@ -1071,7 +1041,7 @@ export class AppImManager {
}
this.scrollable.container.append(this.chatInner);
this.scrollable.attachSentinels();
//this.scrollable.attachSentinels();
//this.scrollable.container.insertBefore(this.chatInner, this.scrollable.container.lastElementChild);
this.lazyLoadQueue.unlock();
@ -1328,34 +1298,40 @@ export class AppImManager { @@ -1328,34 +1298,40 @@ export class AppImManager {
public renderMessagesQueue(message: any, bubble: HTMLDivElement, reverse: boolean) {
let promises: Promise<any>[] = [];
(Array.from(bubble.querySelectorAll('img, image, video')) as HTMLImageElement[]).forEach(el => {
if(el.tagName == 'VIDEO') {
(Array.from(bubble.querySelectorAll('img, video')) as HTMLImageElement[]).forEach(el => {
if(el instanceof HTMLVideoElement) {
let source = el.firstElementChild as HTMLSourceElement;
if(!source || !source.src) {
this.log.warn('no source', el, source, 'src', source.src);
return;
}
} else if(el.complete || (!el.src && !el.getAttribute('href'))) return;
} else if(el.readyState >= 4) return;
} else if(el.complete || !el.src) return;
let src = el.src || el.getAttributeNS(null, 'href');
let src = el.src;
let promise = new Promise((resolve, reject) => {
if(el.tagName == 'VIDEO') {
el.addEventListener('loadeddata', () => {
clearTimeout(timeout);
resolve();
});
let r: () => boolean;
let onLoad = () => {
clearTimeout(timeout);
resolve();
};
if(el instanceof HTMLVideoElement) {
el.addEventListener('loadeddata', onLoad);
r = () => el.readyState >= 4;
} else {
el.addEventListener('load', () => {
clearTimeout(timeout);
resolve();
});
el.addEventListener('load', onLoad);
r = () => el.complete;
}
// for safari
let c = () => r() ? onLoad() : window.requestAnimationFrame(c);
window.requestAnimationFrame(c);
let timeout = setTimeout(() => {
console.log('did not called', el, el.parentElement, el.complete, src);
reject();
}, 5000);
}, 1500);
});
promises.push(promise);
@ -2092,7 +2068,7 @@ export class AppImManager { @@ -2092,7 +2068,7 @@ export class AppImManager {
avatarElem.setAttribute('peer-title', message.fwd_from.from_name);
}
avatarElem.setAttribute('peer', '' + ((message.fwd_from ? message.fwdFromID : message.fromID) || 0));
avatarElem.setAttribute('peer', '' + ((message.fwd_from && this.peerID == this.myID ? message.fwdFromID : message.fromID) || 0));
this.log('exec loadDialogPhoto', message);
@ -2151,43 +2127,28 @@ export class AppImManager { @@ -2151,43 +2127,28 @@ export class AppImManager {
console.time('appImManager render history');
let firstLoad = !!this.setPeerPromise && false;
let peerID = this.peerID;
return new Promise<boolean>((resolve, reject) => {
let method = (reverse ? history.shift : history.pop).bind(history);
let r = () => {
if(this.peerID != peerID) {
return reject('peer changed');
}
let msgID = method();
if(!msgID) {
return;
}
let message = appMessagesManager.getMessage(msgID);
this.renderMessage(message, reverse, true);
firstLoad ? window.requestAnimationFrame(r) : r();
};
firstLoad ? window.requestAnimationFrame(r) : r();
let realLength = this.scrollable.length;
let previousScrollHeightMinusTop: number;
if(realLength > 0 && reverse) {
this.messagesQueueOnRender = () => {
let scrollTop = realLength ? this.scrollable.scrollTop : 0;
previousScrollHeightMinusTop = realLength ? this.scrollable.scrollHeight - scrollTop : 0;
let scrollTop = this.scrollable.scrollTop;
previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop;
this.log('performHistoryResult: messagesQueueOnRender, scrollTop:', scrollTop, previousScrollHeightMinusTop);
this.messagesQueueOnRender = undefined;
};
}
while(history.length) {
let message = appMessagesManager.getMessage(method());
this.renderMessage(message, reverse, true);
}
(this.messagesQueuePromise || Promise.resolve()).then(() => {
if(realLength > 0 && reverse && previousScrollHeightMinusTop !== undefined) {
if(previousScrollHeightMinusTop !== undefined) {
this.log('performHistoryResult: will set scrollTop', this.scrollable.scrollHeight, previousScrollHeightMinusTop);
this.scrollable.scrollTop = this.scrollable.scrollHeight - previousScrollHeightMinusTop;
}

21
src/lib/appManagers/appMediaViewer.ts

@ -57,6 +57,8 @@ export class AppMediaViewer { @@ -57,6 +57,8 @@ export class AppMediaViewer {
private reverse = false; // reverse means next = higher msgid
private needLoadMore = true;
private pageEl = document.getElementById('page-chats') as HTMLDivElement;
constructor() {
this.log = logger('AMV');
@ -568,7 +570,7 @@ export class AppMediaViewer { @@ -568,7 +570,7 @@ export class AppMediaViewer {
return promise;
}
public updateMediaSource(target: HTMLElement, url: string, tagName: 'source' | 'image') {
public updateMediaSource(target: HTMLElement, url: string, tagName: 'source' | 'img') {
//if(target instanceof SVGSVGElement) {
let el = target.querySelector(tagName) as HTMLElement;
renderImageFromUrl(el, url);
@ -665,17 +667,20 @@ export class AppMediaViewer { @@ -665,17 +667,20 @@ export class AppMediaViewer {
////////this.log('wasActive:', wasActive);
if(useContainerAsTarget) {
target = target.querySelector('img, video') || target;
}
let mover = this.content.mover;
let maxWidth = appPhotosManager.windowW - 16;
//let maxWidth = appPhotosManager.windowW - 16;
let maxWidth = this.pageEl.scrollWidth - 16;
let maxHeight = appPhotosManager.windowH - 100;
if(isVideo) {
let size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight);
appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight);
////////this.log('will wrap video', media, size);
if(useContainerAsTarget) target = target.querySelector('img, video') || target;
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
//return; // set and don't move
//if(wasActive) return;
@ -745,8 +750,6 @@ export class AppMediaViewer { @@ -745,8 +750,6 @@ export class AppMediaViewer {
} else {
let size = appPhotosManager.setAttachmentSize(media.id, container, maxWidth, maxHeight);
if(useContainerAsTarget) target = target.querySelector('img, video') || target;
let afterTimeout = this.setMoverToTarget(target, false, fromRight);
//return; // set and don't move
//if(wasActive) return;
@ -766,8 +769,8 @@ export class AppMediaViewer { @@ -766,8 +769,8 @@ export class AppMediaViewer {
let url = media.url;
if(target instanceof SVGSVGElement) {
this.updateMediaSource(target, url, 'image');
this.updateMediaSource(mover, url, 'image');
this.updateMediaSource(target, url, 'img');
this.updateMediaSource(mover, url, 'img');
} else {
let aspecter = mover.firstElementChild;
let image = aspecter.firstElementChild as HTMLImageElement;

8
src/lib/appManagers/appMessagesManager.ts

@ -224,7 +224,7 @@ export class AppMessagesManager { @@ -224,7 +224,7 @@ export class AppMessagesManager {
}).catch(resolve);
});
//setInterval(() => this.saveState(), 10000);
setInterval(() => this.saveState(), 10000);
}
public saveState() {
@ -1166,7 +1166,7 @@ export class AppMessagesManager { @@ -1166,7 +1166,7 @@ export class AppMessagesManager {
} else {
let doc = messageMedia.document;
appDocsManager.saveDoc(doc);
inputMedia = appDocsManager.getInputByID(doc.id);
inputMedia = appDocsManager.getMediaInputByID(doc.id);
}
inputs.push({
@ -3002,7 +3002,11 @@ export class AppMessagesManager { @@ -3002,7 +3002,11 @@ export class AppMessagesManager {
if(messageID > maxID) {
continue;
}
message = this.messagesStorage[messageID];
if(!message) {
continue;
}
if(message.pFlags.out != isOut) {
continue;

50
src/lib/appManagers/appPhotosManager.ts

@ -176,8 +176,8 @@ export class AppPhotosManager { @@ -176,8 +176,8 @@ export class AppPhotosManager {
};
});
}
public setAttachmentPreview(bytes: Uint8Array, element: HTMLElement | SVGSVGElement, isSticker = false, background = false) {
public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
let arr: Uint8Array;
if(!isSticker) {
arr = AppPhotosManager.jf.concat(bytes.slice(3), AppPhotosManager.Df);
@ -187,9 +187,7 @@ export class AppPhotosManager { @@ -187,9 +187,7 @@ export class AppPhotosManager {
arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
}
//console.log('setAttachmentPreview', bytes, arr, div, isSticker);
let blob = new Blob([arr], {type: "image/jpeg"});
//console.log('getPreviewURLFromBytes', bytes, arr, div, isSticker);
/* let reader = new FileReader();
reader.onloadend = () => {
@ -197,8 +195,19 @@ export class AppPhotosManager { @@ -197,8 +195,19 @@ export class AppPhotosManager {
};
reader.readAsDataURL(blob); */
let blob = new Blob([arr], {type: "image/jpeg"});
return URL.createObjectURL(blob);
}
public getPreviewURLFromThumb(thumb: any, isSticker = false) {
return thumb.url ?? (thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
}
public setAttachmentPreview(bytes: Uint8Array | number[], element: HTMLElement | SVGForeignObjectElement, isSticker = false, background = false) {
let url = this.getPreviewURLFromBytes(bytes, isSticker);
if(background) {
let url = URL.createObjectURL(blob);
let img = new Image();
img.src = url;
img.addEventListener('load', () => {
@ -206,30 +215,23 @@ export class AppPhotosManager { @@ -206,30 +215,23 @@ export class AppPhotosManager {
});
return element;
//element.style.backgroundImage = 'url(' + url + ')';
} else {
if(element instanceof SVGSVGElement) {
let image = element.firstElementChild as SVGImageElement || document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttributeNS(null, 'href', URL.createObjectURL(blob));
element.append(image);
return image;
} else if(element instanceof HTMLImageElement) {
element.src = URL.createObjectURL(blob);
if(element instanceof HTMLImageElement) {
element.src = url;
return element;
} else {
let img = new Image();
img.style.width = '100%';
img.style.height = '100%';
/* img.style.width = '100%';
img.style.height = '100%'; */
img.src = URL.createObjectURL(blob);
img.src = url;
element.append(img);
return img;
}
}
}
public setAttachmentSize(photoID: any, element: HTMLElement | SVGSVGElement, boxWidth = 380, boxHeight = 380, isSticker = false) {
public setAttachmentSize(photoID: any, element: HTMLElement | SVGForeignObjectElement, boxWidth = 380, boxHeight = 380, isSticker = false) {
let photo: /* MTDocument | MTPhoto */any = null;
if(typeof(photoID) === 'string') {
@ -243,7 +245,7 @@ export class AppPhotosManager { @@ -243,7 +245,7 @@ export class AppPhotosManager {
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
let sizes = photo.sizes || photo.thumbs;
if((!photo.downloaded || (isSticker && photo.animated)) && sizes && sizes[0].bytes) {
if(!photo.downloaded && !isSticker && sizes && sizes[0].bytes) {
this.setAttachmentPreview(sizes[0].bytes, element, isSticker);
}
@ -258,17 +260,11 @@ export class AppPhotosManager { @@ -258,17 +260,11 @@ export class AppPhotosManager {
}
let {w, h} = calcImageInBox(width, height, boxWidth, boxHeight);
if(element instanceof SVGSVGElement) {
if(element instanceof SVGForeignObjectElement) {
element.setAttributeNS(null, 'width', '' + w);
element.setAttributeNS(null, 'height', '' + h);
//console.log('set dimensions to svg element:', element, w, h);
if(element.firstElementChild) {
let imageSvg = element.firstElementChild as SVGImageElement;
imageSvg.setAttributeNS(null, 'width', '' + w);
imageSvg.setAttributeNS(null, 'height', '' + h);
}
} else {
element.style.width = w + 'px';
element.style.height = h + 'px';

4
src/lib/appManagers/appSidebarRight.ts

@ -111,14 +111,14 @@ class AppSidebarRight { @@ -111,14 +111,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', 'SR');
this.scroll = new Scrollable(this.profileContainer, 'y', 'SR', undefined, 400);
this.scroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && this.sharedMediaSelected.childElementCount/* && false */) {
this.log('onScrolledBottom will load media');
this.loadSidebarMedia(true);
}
};
this.scroll.attachSentinels(undefined, 400);
//this.scroll.attachSentinels(undefined, 400);
horizontalMenu(this.profileTabs, container, (id, tabContent) => {
if(this.prevTabID == id) return;

55
src/lib/appManagers/appStickersManager.ts

@ -51,6 +51,8 @@ class AppStickersManager { @@ -51,6 +51,8 @@ class AppStickersManager {
private stickerSets: {
[stickerSetID: string]: MTStickerSetFull
} = {};
private saveSetsTimeout: number;
constructor() {
AppStorage.get<{
@ -59,17 +61,15 @@ class AppStickersManager { @@ -59,17 +61,15 @@ class AppStickersManager {
if(sets) {
for(let id in sets) {
let set = sets[id];
set.documents.forEach(doc => {
this.saveSticker(doc);
});
this.saveStickers(set.documents);
}
this.stickerSets = sets;
}
if(!this.stickerSets['emoji']) {
//if(!this.stickerSets['emoji']) {
this.getStickerSet({id: 'emoji', access_hash: ''});
}
//}
});
}
@ -81,6 +81,12 @@ class AppStickersManager { @@ -81,6 +81,12 @@ class AppStickersManager {
return doc;
}
public saveStickers(docs: MTDocument[]) {
docs.forEach((doc, idx) => {
docs[idx] = this.saveSticker(doc);
});
}
public getSticker(fileID: string) {
return this.documents[fileID];
@ -115,6 +121,20 @@ class AppStickersManager { @@ -115,6 +121,20 @@ class AppStickersManager {
return stickerSet;
}
public async getRecentStickers() {
let res: {
_: string,
hash: number,
packs: any[],
stickers: MTDocument[],
dates: number[]
} = await apiManager.invokeApi('messages.getRecentStickers', {flags: 0, hash: 0});
this.saveStickers(res.stickers);
return res;
}
public getAnimatedEmojiSticker(emoji: string) {
let stickerSet = this.stickerSets.emoji;
@ -122,7 +142,7 @@ class AppStickersManager { @@ -122,7 +142,7 @@ class AppStickersManager {
return stickerSet.documents.find(doc => doc.stickerEmojiRaw == emoji);
}
public async saveStickerSet(res: {
public saveStickerSet(res: {
_: "messages.stickerSet",
set: MTStickerSet,
packs: any[],
@ -136,12 +156,18 @@ class AppStickersManager { @@ -136,12 +156,18 @@ class AppStickersManager {
documents: res.documents
};
res.documents.forEach(this.saveSticker.bind(this));
this.saveStickers(res.documents);
//console.log('stickers wrote', this.stickerSets);
await AppStorage.set({
stickerSets: this.stickerSets
});
if(this.saveSetsTimeout) return;
this.saveSetsTimeout = setTimeout(() => {
AppStorage.set({
stickerSets: this.stickerSets
});
this.saveSetsTimeout = 0;
}, 0);
/* AppStorage.get('stickerSets').then((sets: any) => {
this.stickerSets = sets;
@ -153,7 +179,9 @@ class AppStickersManager { @@ -153,7 +179,9 @@ class AppStickersManager {
let thumb = stickerSet.thumb;
let dcID = stickerSet.thumb_dc_id;
let promise = apiFileManager.downloadSmallFile({
let isAnimated = stickerSet.pFlags?.animated;
let promise = apiFileManager.downloadFile(dcID, {
_: 'inputStickerSetThumb',
stickerset: {
_: 'inputStickerSetID',
@ -162,7 +190,10 @@ class AppStickersManager { @@ -162,7 +190,10 @@ class AppStickersManager {
},
volume_id: thumb.location.volume_id,
local_id: thumb.location.local_id
}, {dcID: dcID});
}, thumb.size, {
stickerType: isAnimated ? 2 : 1,
mimeType: isAnimated ? "application/x-tgsticker" : 'image/webp'
});
return promise;
}

94
src/lib/appManagers/appWebpManager.ts

@ -1,31 +1,21 @@ @@ -1,31 +1,21 @@
// @ts-ignore
//import createWorker from 'offscreen-canvas/create-worker';
class AppWebpManager {
public webpMachine: any = null;
public loaded: Promise<void>;
public busyPromise: Promise<string>;
public queue: {bytes: Uint8Array, img: HTMLImageElement, callback: (url: string) => void}[] = [];
//public worker: any;
public webpSupport: Promise<boolean> = null;
private webpMachine: any = null;
private loaded: Promise<void>;
private busyPromise: Promise<Uint8Array | void>;
private queue: {bytes: Uint8Array, callback: (res: Uint8Array) => void}[] = [];
private testPromise: Promise<boolean> = null;
public webpSupport = false;
constructor() {
//let canvas = document.createElement('canvas');
//console.log('got message from worker:', canvas.toDataURL());
/* this.worker = createWorker(canvas, '/webp.bundle.js', (e: any) => {
// Messages from the worker
console.log('got message from worker:', e, canvas.toDataURL());
}); */
this.webpSupported().then(res => {
});
this.testWebpSupport();
}
public loadWebpHero() {
private loadWebpHero() {
if(this.loaded) return this.loaded;
this.loaded = new Promise(async(resolve, reject) => {
let res = await this.webpSupported();
let res = await this.testWebpSupport();
if(!res) {
(window as any).webpLoaded = () => {
@ -46,17 +36,16 @@ class AppWebpManager { @@ -46,17 +36,16 @@ class AppWebpManager {
});
}
convert(bytes: Uint8Array): Promise<string> {
private convert(bytes: Uint8Array): AppWebpManager['busyPromise'] {
return this.webpMachine.decode(bytes);
//return this.worker.post({message: 'webpBytes', bytes});
}
async processQueue() {
private async processQueue() {
if(this.busyPromise) return;
this.busyPromise = Promise.resolve('');
this.busyPromise = Promise.resolve();
let {img, bytes, callback} = this.queue.pop();
let {bytes, callback} = this.queue.pop();
if(!this.loaded) {
this.loadWebpHero();
@ -65,13 +54,11 @@ class AppWebpManager { @@ -65,13 +54,11 @@ class AppWebpManager {
await this.loaded;
this.busyPromise = this.convert(bytes);
let url = await this.busyPromise;
let imgTemp = new Image();
imgTemp.src = url;
imgTemp.onload = () => {
img.src = imgTemp.src;
};
callback(url);
let res = await this.busyPromise;
console.log('converted webp', res);
callback(res as Uint8Array);
this.busyPromise = null;
@ -80,42 +67,33 @@ class AppWebpManager { @@ -80,42 +67,33 @@ class AppWebpManager {
}
}
webpSupported() {
if(this.webpSupport) return this.webpSupport;
public testWebpSupport() {
if(this.testPromise) return this.testPromise;
return this.webpSupport = new Promise((resolve, reject) => {
var webP = new Image();
return this.testPromise = new Promise((resolve, reject) => {
let webP = new Image();
webP.src = 'data:image/webp;base64,UklGRi4AAABXRUJQVlA4TCEAAAAvAUAAEB8wAiMw' +
'AgSSNtse/cXjxyCCmrYNWPwmHRH9jwMA';
webP.onload = webP.onerror = () => {
resolve(webP.height === 2);
resolve(this.webpSupport = webP.height === 2/* && false */);
};
});
}
async polyfillImage(img: HTMLImageElement, blob: Blob) {
/* console.log('polyfillImage', this);
return this.webpMachine.polyfillImage(image); */
//if(await this.webpMachine.webpSupport) {
if(await this.webpSupport) {
let url = URL.createObjectURL(blob);
img.src = url;
return url;
}
public isSupported() {
return this.webpSupport;
}
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener('loadend', (e) => {
// @ts-ignore
let bytes = new Uint8Array(e.srcElement.result);
this.queue.push({bytes, img, callback: resolve});
this.processQueue();
});
reader.readAsArrayBuffer(blob);
public convertToPng(bytes: Uint8Array) {
console.warn('convertToPng!');
return new Promise<Uint8Array>((resolve, reject) => {
// @ts-ignore
this.queue.push({bytes, callback: resolve});
this.processQueue();
});
}
}
export default new AppWebpManager();
const appWebpManager = new AppWebpManager();
(window as any).appWebpManager = appWebpManager;
export default appWebpManager;

18
src/lib/bin_utils.ts

@ -342,24 +342,6 @@ export function longFromInts(high: number, low: number) { @@ -342,24 +342,6 @@ export function longFromInts(high: number, low: number) {
return bigint(high).shiftLeft(32).add(bigint(low)).toString(10);
}
export function intToUint(val: number | string) {
if(typeof(val) === 'string') val = parseInt(val);
/* if(val < 0) {
val = val + 4294967296;
} */
return val;
}
export function uintToInt(val: number) {
/* if(val > 2147483647) {
val = val - 4294967296;
} */
return val;
}
export function addPadding(bytes: any, blockSize: number = 16, zeroes?: boolean, full = false, prepend = false) {
let len = bytes.byteLength || bytes.length;
let needPadding = blockSize - (len % blockSize);

12
src/lib/filemanager.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import {blobSafeMimeType, blobConstruct, bytesToBase64} from './bin_utils';
import {blobConstruct} from './bin_utils';
/* import 'web-streams-polyfill/ponyfill';
// @ts-ignore
@ -139,14 +139,6 @@ class FileManager { @@ -139,14 +139,6 @@ class FileManager {
return fakeFileWriter;
}
public getFileCorrectUrl(fileData: Blob | number[], mimeType: string): string {
var safeMimeType = blobSafeMimeType(mimeType);
if(fileData instanceof Blob) {
return URL.createObjectURL(fileData);
}
return 'data:' + safeMimeType + ';base64,' + bytesToBase64(fileData);
}
public download(blob: Blob, mimeType: string, fileName: string) {
if(window.navigator && navigator.msSaveBlob !== undefined) {
@ -180,7 +172,7 @@ class FileManager { @@ -180,7 +172,7 @@ class FileManager {
return;
}
let url = this.getFileCorrectUrl(blob, mimeType);
let url = URL.createObjectURL(blob);
var anchor = document.createElementNS('http://www.w3.org/1999/xhtml', 'a') as HTMLAnchorElement;
anchor.href = url as string;
anchor.download = fileName;

2
src/lib/idb.ts

@ -51,6 +51,8 @@ class IdbFileStorage { @@ -51,6 +51,8 @@ class IdbFileStorage {
request.onsuccess = (event) => {
finished = true;
var db = request.result;
console.log('Opened IndexedDB');
db.onerror = (error) => {
this.storageIsAvailable = false;

8
src/lib/lottieLoader.ts

@ -96,7 +96,7 @@ class LottieLoader { @@ -96,7 +96,7 @@ class LottieLoader {
params.renderer = 'svg';
//}
params.rendererSettings = {
let rendererSettings = {
//context: context, // the canvas context
//preserveAspectRatio: 'xMinYMin slice', // Supports the same options as the svg element's preserveAspectRatio property
clearCanvas: true,
@ -104,6 +104,12 @@ class LottieLoader { @@ -104,6 +104,12 @@ class LottieLoader {
hideOnTransparent: true, //Boolean, only svg renderer, hides elements when opacity reaches 0 (defaults to true),
};
if(params.rendererSettings) {
params.rendererSettings = Object.assign(params.rendererSettings, rendererSettings);
} else {
params.rendererSettings = rendererSettings;
}
if(!this.lottie) {
if(!this.loaded) this.loadLottie();
await this.loaded;

122
src/lib/mtproto/apiFileManager.ts

@ -5,6 +5,7 @@ import FileManager from "../filemanager"; @@ -5,6 +5,7 @@ import FileManager from "../filemanager";
//import apiManager from "./apiManager";
import apiManager from "./mtprotoworker";
import { logger, deferredPromise, CancellablePromise } from "../polyfill";
import appWebpManager from "../appManagers/appWebpManager";
export class ApiFileManager {
public cachedSavePromises: {
@ -14,7 +15,7 @@ export class ApiFileManager { @@ -14,7 +15,7 @@ export class ApiFileManager {
[fileName: string]: any
} = {};
public cachedDownloads: {
[fileName: string]: any
[fileName: string]: Blob
} = {};
/* public indexedKeys: Set<string> = new Set();
@ -84,25 +85,38 @@ export class ApiFileManager { @@ -84,25 +85,38 @@ export class ApiFileManager {
});
}
public getFileName(location: any) {
public getFileName(location: any, options?: Partial<{
stickerType: number
}>) {
switch(location._) {
case 'inputDocumentFileLocation':
var fileName = (location.file_name as string || '').split('.');
var ext: string = fileName[fileName.length - 1] || '';
case 'inputDocumentFileLocation': {
let fileName = (location.file_name as string || '').split('.');
let ext = fileName[fileName.length - 1] || '';
if(options?.stickerType == 1 && !appWebpManager.isSupported()) {
ext += '.png'
}
var versionPart = location.version ? ('v' + location.version) : '';
return (fileName[0] ? fileName[0] + '_' : '') + location.id + versionPart + (ext ? '.' + ext : ext);
let thumbPart = location.thumb_size ? '_' + location.thumb_size : '';
return (fileName[0] ? fileName[0] + '_' : '') + location.id + thumbPart + (ext ? '.' + ext : ext);
}
default:
default: {
if(!location.volume_id && !location.file_reference) {
this.log.trace('Empty location', location);
}
let ext = 'jpg';
if(options?.stickerType == 1 && !appWebpManager.isSupported()) {
ext += '.png'
}
if(location.volume_id) {
return location.volume_id + '_' + location.local_id + '.' + ext;
} else {
return location.id + '_' + location.access_hash + '.' + ext;
}
}
}
}
@ -145,10 +159,11 @@ export class ApiFileManager { @@ -145,10 +159,11 @@ export class ApiFileManager {
return this.cachedSavePromises[fileName];
}
public downloadSmallFile(location: any, options: {
mimeType?: string,
dcID?: number,
} = {}): Promise<Blob> {
public downloadSmallFile(location: any, options: Partial<{
mimeType: string,
dcID: number,
stickerType: number
}> = {}): Promise<Blob> {
if(!FileManager.isAvailable()) {
return Promise.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'});
}
@ -159,10 +174,16 @@ export class ApiFileManager { @@ -159,10 +174,16 @@ export class ApiFileManager {
//this.log('downloadSmallFile', location, options);
let processSticker = false;
if(options.stickerType == 1 && !appWebpManager.isSupported()) {
processSticker = true;
options.mimeType = 'image/png';
}
let dcID = options.dcID || location.dc_id;
let mimeType = options.mimeType || 'image/jpeg';
var fileName = this.getFileName(location);
var cachedPromise = this.cachedSavePromises[fileName] || this.cachedDownloadPromises[fileName];
let fileName = this.getFileName(location, options);
let cachedPromise = this.cachedSavePromises[fileName] || this.cachedDownloadPromises[fileName];
//this.log('downloadSmallFile!', location, options, fileName, cachedPromise);
@ -170,13 +191,14 @@ export class ApiFileManager { @@ -170,13 +191,14 @@ export class ApiFileManager {
return cachedPromise;
}
var fileStorage = this.getFileStorage();
let fileStorage = this.getFileStorage();
return this.cachedDownloadPromises[fileName] = fileStorage.getFile(fileName).then((blob: any) => {
return this.cachedDownloadPromises[fileName] = fileStorage.getFile(fileName).then((blob) => {
//throw '';
return this.cachedDownloads[fileName] = blob;
}, () => {
var downloadPromise = this.downloadRequest(dcID, () => {
var inputLocation = location;
}).catch(() => {
let downloadPromise = this.downloadRequest(dcID, () => {
let inputLocation = location;
if(!inputLocation._ || inputLocation._ == 'fileLocation') {
inputLocation = Object.assign({}, location, {_: 'inputFileLocation'});
}
@ -196,18 +218,17 @@ export class ApiFileManager { @@ -196,18 +218,17 @@ export class ApiFileManager {
});
}, dcID);
var processDownloaded = (bytes: Uint8Array) => {
let processDownloaded = (bytes: Uint8Array) => {
//this.log('processDownloaded', location, bytes);
return Promise.resolve(bytes);
/* if(!location.sticker || WebpManager.isWebpSupported()) {
return qSync.when(bytes);
if(processSticker) {
return appWebpManager.convertToPng(bytes);
}
return WebpManager.getPngBlobFromWebp(bytes); */
return Promise.resolve(bytes);
};
return fileStorage.getFileWriter(fileName, mimeType).then((fileWriter: any) => {
return fileStorage.getFileWriter(fileName, mimeType).then(fileWriter => {
return downloadPromise.then((result: any) => {
return processDownloaded(result.bytes).then((proccessedResult) => {
return FileManager.write(fileWriter, proccessedResult).then(() => {
@ -236,12 +257,12 @@ export class ApiFileManager { @@ -236,12 +257,12 @@ export class ApiFileManager {
});
} */
public downloadFile(dcID: number, location: any, size: number, options: {
mimeType?: string,
dcID?: number,
toFileEntry?: any,
limitPart?: number
} = {}): CancellablePromise<Blob> {
public downloadFile(dcID: number, location: any, size: number, options: Partial<{
mimeType: string,
toFileEntry: any,
limitPart: number,
stickerType: number
}> = {}): CancellablePromise<Blob> {
if(!FileManager.isAvailable()) {
return Promise.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'});
}
@ -250,11 +271,21 @@ export class ApiFileManager { @@ -250,11 +271,21 @@ export class ApiFileManager {
this.getIndexedKeys();
} */
let processSticker = false;
if(options.stickerType == 1 && !appWebpManager.isSupported()) {
if(options.toFileEntry || size > 524288) {
delete options.stickerType;
} else {
processSticker = true;
options.mimeType = 'image/png';
}
}
// this.log('Dload file', dcID, location, size)
var fileName = this.getFileName(location);
var toFileEntry = options.toFileEntry || null;
var cachedPromise = this.cachedSavePromises[fileName] || this.cachedDownloadPromises[fileName];
var fileStorage = this.getFileStorage();
let fileName = this.getFileName(location, options);
let toFileEntry = options.toFileEntry || null;
let cachedPromise = this.cachedSavePromises[fileName] || this.cachedDownloadPromises[fileName];
let fileStorage = this.getFileStorage();
//this.log('downloadFile', fileStorage.name, fileName, fileName.length, location, arguments);
@ -272,7 +303,7 @@ export class ApiFileManager { @@ -272,7 +303,7 @@ export class ApiFileManager {
if(blob.size < size) {
this.log('downloadFile need to deleteFile, wrong size:', blob.size, size);
return this.deleteFile(location).then(() => {
return this.deleteFile(fileName).then(() => {
return this.downloadFile(dcID, location, size, options);
}).catch(() => {
return this.downloadFile(dcID, location, size, options);
@ -303,10 +334,11 @@ export class ApiFileManager { @@ -303,10 +334,11 @@ export class ApiFileManager {
fileStorage.getFile(fileName, size).then(async(blob: Blob) => {
//this.log('is that i wanted');
//throw '';
if(blob.size < size) {
this.log('downloadFile need to deleteFile 2, wrong size:', blob.size, size);
await this.deleteFile(location);
await this.deleteFile(fileName);
throw false;
}
@ -322,13 +354,12 @@ export class ApiFileManager { @@ -322,13 +354,12 @@ export class ApiFileManager {
//var fileWriterPromise = toFileEntry ? FileManager.getFileWriter(toFileEntry) : fileStorage.getFileWriter(fileName, mimeType);
var fileWriterPromise = toFileEntry ? Promise.resolve(toFileEntry) : fileStorage.getFileWriter(fileName, mimeType);
var processDownloaded = (bytes: any) => {
return Promise.resolve(bytes);
/* if(!processSticker) {
return Promise.resolve(bytes);
var processDownloaded = (bytes: Uint8Array) => {
if(processSticker) {
return appWebpManager.convertToPng(bytes);
}
return WebpManager.getPngBlobFromWebp(bytes); */
return Promise.resolve(bytes);
};
fileWriterPromise.then((fileWriter: any) => {
@ -391,7 +422,7 @@ export class ApiFileManager { @@ -391,7 +422,7 @@ export class ApiFileManager {
return Promise.resolve();
}
return processDownloaded(result.bytes).then((processedResult: Uint8Array) => {
return processDownloaded(result.bytes).then((processedResult) => {
return FileManager.write(fileWriter, processedResult).then(() => {
writeFileDeferred.resolve();
}, errorHandler).then(() => {
@ -438,12 +469,13 @@ export class ApiFileManager { @@ -438,12 +469,13 @@ export class ApiFileManager {
return deferred;
}
public deleteFile(fileName: any) {
fileName = typeof(fileName) == 'string' ? fileName : this.getFileName(fileName);
public deleteFile(fileName: string) {
this.log('will delete file:', fileName);
delete this.cachedDownloadPromises[fileName];
delete this.cachedDownloads[fileName];
delete this.cachedSavePromises[fileName];
return this.getFileStorage().deleteFile(fileName);
}

9
src/lib/mtproto/networker.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import {isObject} from '../bin_utils';
import {convertToUint8Array,
bufferConcat, nextRandomInt, bytesToHex, longToBytes,
bytesCmp, uintToInt, bigStringInt} from '../bin_utils';
bytesCmp, bigStringInt} from '../bin_utils';
import {TLDeserialization, TLSerialization} from './tl_utils';
import CryptoWorker from '../crypto/cryptoworker';
import AppStorage from '../storage';
@ -296,8 +296,7 @@ class MTPNetworker { @@ -296,8 +296,7 @@ class MTPNetworker {
var isClean = this.cleanupSent();
//this.log('Check lp', this.longPollPending, tsNow(), this.dcID, isClean, this);
if((this.longPollPending && Date.now() < this.longPollPending) ||
this.offline ||
NetworkerFactory.akStopped) {
this.offline) {
//this.log('No lp this time');
return false;
}
@ -502,7 +501,7 @@ class MTPNetworker { @@ -502,7 +501,7 @@ class MTPNetworker {
public performScheduledRequest() {
// this.log('scheduled', this.dcID, this.iii)
if(this.offline || NetworkerFactory.akStopped) {
if(this.offline) {
this.log('Cancel scheduled');
return false;
}
@ -1027,7 +1026,7 @@ class MTPNetworker { @@ -1027,7 +1026,7 @@ class MTPNetworker {
public processError(rawError: {error_message: string, error_code: number}) {
var matches = (rawError.error_message || '').match(/^([A-Z_0-9]+\b)(: (.+))?/) || [];
rawError.error_code = uintToInt(rawError.error_code);
rawError.error_code = rawError.error_code;
return {
code: !rawError.error_code || rawError.error_code <= 0 ? 500 : rawError.error_code,

18
src/lib/mtproto/networkerFactory.ts

@ -2,24 +2,6 @@ import { MTPNetworker } from "./networker"; @@ -2,24 +2,6 @@ import { MTPNetworker } from "./networker";
export class NetworkerFactory {
public updatesProcessor: (obj: any, bool: boolean) => void = null;
//public offlineInited = false;
public akStopped = false;
/* public startAll() {
if(this.akStopped) {
this.akStopped = false;
if(this.updatesProcessor) {
this.updatesProcessor({
_: 'new_session_created'
}, true);
}
}
}
public stopAll() {
this.akStopped = true;
} */
public setUpdatesProcessor(callback: (obj: any, bool: boolean) => void) {
this.updatesProcessor = callback;

14
src/lib/mtproto/tl_utils.ts

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import {bigint, intToUint, bigStringInt, bytesToHex, uintToInt, isObject} from '../bin_utils';
import {bigint, bigStringInt, bytesToHex, isObject} from '../bin_utils';
import Schema from './schema';
/// #if MTPROTO_WORKER
@ -131,8 +131,8 @@ class TLSerialization { @@ -131,8 +131,8 @@ class TLSerialization {
}
var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000));
this.writeInt(intToUint(divRem[1].intValue()), (field || '') + ':long[low]');
this.writeInt(intToUint(divRem[0].intValue()), (field || '') + ':long[high]');
this.writeInt(divRem[1].intValue(), (field || '') + ':long[low]');
this.writeInt(divRem[0].intValue(), (field || '') + ':long[high]');
}
public storeDouble(f: any, field?: string) {
@ -250,7 +250,7 @@ class TLSerialization { @@ -250,7 +250,7 @@ class TLSerialization {
throw new Error('No method ' + methodName + ' found');
}
this.storeInt(intToUint(methodData.id), methodName + '[id]');
this.storeInt(methodData.id, methodName + '[id]');
var param, type;
var i, condType;
@ -347,7 +347,7 @@ class TLSerialization { @@ -347,7 +347,7 @@ class TLSerialization {
}
if(!isBare) {
this.writeInt(intToUint(constructorData.id), field + '[' + predicate + '][id]');
this.writeInt(constructorData.id, field + '[' + predicate + '][id]');
}
var param, type: string;
@ -601,7 +601,7 @@ class TLDeserialization { @@ -601,7 +601,7 @@ class TLDeserialization {
if(type.substr(0, 6) == 'Vector' || type.substr(0, 6) == 'vector') {
if(type.charAt(0) == 'V') {
var constructor = this.readInt(field + '[id]');
var constructorCmp = uintToInt(constructor);
var constructorCmp = constructor;
if(constructorCmp == gzipPacked) { // Gzip packed
var compressed = this.fetchBytes(field + '[packed_string]');
@ -657,7 +657,7 @@ class TLDeserialization { @@ -657,7 +657,7 @@ class TLDeserialization {
}
} else {
var constructor = this.readInt(field + '[id]');
var constructorCmp = uintToInt(constructor);
var constructorCmp = constructor;
if(constructorCmp == gzipPacked) { // Gzip packed
var compressed = this.fetchBytes(field + '[packed_string]');

6
src/lib/utils.js

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

44
src/lib/webp.ts

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
import {Webp} from "webp-hero/libwebp/dist/webp.js"
import {detectWebpSupport} from "webp-hero/dist/detect-webp-support.js"
const relax = () => new Promise(resolve => requestAnimationFrame(resolve))
const relax = () => new Promise(resolve => requestAnimationFrame(resolve));
export class WebpMachineError extends Error {}
@ -11,34 +10,39 @@ export class WebpMachineError extends Error {} @@ -11,34 +10,39 @@ export class WebpMachineError extends Error {}
* - can only decode images one-at-a-time (otherwise will throw busy error)
*/
export class WebpMachine {
private readonly webp: Webp
private readonly webpSupport: Promise<boolean>
private busy = false
private cache: {[key: string]: string} = {}
constructor({
webp = new Webp(),
webpSupport = detectWebpSupport()
} = {}) {
this.webp = webp;
private readonly webp: Webp;
private busy = false;
constructor() {
this.webp = new Webp();
this.webp.Module.doNotCaptureKeyboard = true;
this.webpSupport = webpSupport;
}
/**
* Decode raw webp data into a png data url
*/
async decode(webpData: Uint8Array): Promise<string> {
decode(webpData: Uint8Array): Promise<Uint8Array> {
if(this.busy) throw new WebpMachineError("cannot decode when already busy");
this.busy = true;
try {
await relax();
const canvas = document.createElement("canvas");
this.webp.setCanvas(canvas);
this.webp.webpToSdl(webpData, webpData.length);
this.busy = false;
return canvas.toDataURL();
return relax().then(() => {
const canvas = document.createElement("canvas");
this.webp.setCanvas(canvas);
this.webp.webpToSdl(webpData, webpData.length);
this.busy = false;
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
let reader = new FileReader();
reader.onload = (event) => {
resolve(new Uint8Array(event.target.result as ArrayBuffer));
};
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
}, 'image/png', 1);
});
});
} catch(error) {
this.busy = false;
error.name = WebpMachineError.name;

75
src/pages/pageAuthCode.ts

@ -28,7 +28,12 @@ let sentTypeElement: HTMLParagraphElement = null; @@ -28,7 +28,12 @@ let sentTypeElement: HTMLParagraphElement = null;
let onFirstMount = (): Promise<any> => {
let needFrame = 0, lastLength = 0;
let animation: /* AnimationItem */any = undefined;
let animation: /* AnimationItem */any;
let idleAnimation: any;
let mTrackingSvg: SVGSVGElement;
let mIdleSvg: SVGSVGElement;
const CODELENGTH = authCode.type.length;
@ -113,6 +118,13 @@ let onFirstMount = (): Promise<any> => { @@ -113,6 +118,13 @@ let onFirstMount = (): Promise<any> => {
});
}
let cleanup = () => {
setTimeout(() => {
if(animation) animation.destroy();
if(idleAnimation) idleAnimation.destroy();
}, 300);
};
let submitCode = (code: string) => {
codeInput.setAttribute('disabled', 'true');
@ -135,7 +147,7 @@ let onFirstMount = (): Promise<any> => { @@ -135,7 +147,7 @@ let onFirstMount = (): Promise<any> => {
});
pageIm.mount();
if(animation) animation.destroy();
cleanup();
break;
case 'auth.authorizationSignUpRequired':
console.log('Registration needed!');
@ -145,7 +157,7 @@ let onFirstMount = (): Promise<any> => { @@ -145,7 +157,7 @@ let onFirstMount = (): Promise<any> => {
'phone_code_hash': authCode.phone_code_hash
});
if(animation) animation.destroy();
cleanup();
break;
default:
codeInput.innerText = response._;
@ -158,7 +170,7 @@ let onFirstMount = (): Promise<any> => { @@ -158,7 +170,7 @@ let onFirstMount = (): Promise<any> => {
case 'SESSION_PASSWORD_NEEDED':
console.warn('pageAuthCode: SESSION_PASSWORD_NEEDED');
err.handled = true;
if(animation) animation.destroy();
cleanup();
pagePassword.mount();
break;
case 'PHONE_CODE_EMPTY':
@ -196,8 +208,14 @@ let onFirstMount = (): Promise<any> => { @@ -196,8 +208,14 @@ let onFirstMount = (): Promise<any> => {
if(!animation) return;
let frame: number;
if(length) frame = Math.round((length > max ? max : length) * (165 / max) + 11.33);
else frame = 0;
if(length) {
frame = Math.round((length > max ? max : length) * (165 / max) + 11.33);
mIdleSvg.style.display = 'none';
mTrackingSvg.style.display = '';
} else {
frame = 0;
}
//animation.playSegments([1, 2]);
let direction = needFrame > frame ? -1 : 1;
@ -217,33 +235,68 @@ let onFirstMount = (): Promise<any> => { @@ -217,33 +235,68 @@ let onFirstMount = (): Promise<any> => {
//animation.goToAndStop(length / max * );
});
let imageDiv = page.pageEl.querySelector('.auth-image');
return Promise.all([
LottieLoader.loadLottie(),
fetch('assets/img/TwoFactorSetupMonkeyIdle.tgs')
.then(res => res.arrayBuffer())
.then(data => apiManager.gzipUncompress<string>(data, true))
.then(str => LottieLoader.loadAnimation({
container: imageDiv,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: JSON.parse(str),
rendererSettings: {
className: 'monkey-idle'
}
}))
.then(_animation => {
idleAnimation = _animation;
mIdleSvg = imageDiv.querySelector('.monkey-idle');
}),
fetch('assets/img/TwoFactorSetupMonkeyTracking.tgs')
.then(res => res.arrayBuffer())
.then(data => apiManager.gzipUncompress<string>(data, true))
.then(str => LottieLoader.loadAnimation({
container: page.pageEl.querySelector('.auth-image'),
container: imageDiv,
renderer: 'svg',
loop: false,
autoplay: false,
animationData: JSON.parse(str)
animationData: JSON.parse(str),
rendererSettings: {
className: 'monkey-tracking'
}
}))
.then(_animation => {
animation = _animation;
animation.setSpeed(1);
//console.log(animation.getDuration(), animation.getDuration(true));
mTrackingSvg = imageDiv.querySelector('.monkey-tracking');
if(!codeInput.value.length) {
mTrackingSvg.style.display = 'none';
}
animation.addEventListener('enterFrame', (e: any) => {
//console.log('enterFrame', e, needFrame);
let currentFrame = Math.round(e.currentTime);
if((e.direction == 1 && currentFrame >= needFrame) ||
(e.direction == -1 && currentFrame <= needFrame)) {
animation.setSpeed(1);
animation.pause();
}
animation.setSpeed(1);
animation.pause();
}
if(currentFrame == 0 && needFrame == 0 && mIdleSvg) {
mTrackingSvg.style.display = 'none';
mIdleSvg.style.display = '';
idleAnimation.stop();
idleAnimation.play();
}
});
})
]);

2
src/pages/pageSignIn.ts

@ -206,6 +206,8 @@ let onFirstMount = () => { @@ -206,6 +206,8 @@ let onFirstMount = () => {
putPreloader(this);
//this.innerHTML = 'PLEASE WAIT...';
return;
let phone_number = telEl.value;
apiManager.invokeApi('auth.sendCode', {
//flags: 0,

1
src/scss/partials/_chat.scss

@ -205,6 +205,7 @@ $time-background: rgba(0, 0, 0, .35); @@ -205,6 +205,7 @@ $time-background: rgba(0, 0, 0, .35);
#attach-file {
&.menu-open {
color: $color-blue;
background-color: transparent;
}
.btn-menu {

16
src/scss/partials/_chatBubble.scss

@ -355,8 +355,6 @@ @@ -355,8 +355,6 @@
position: relative;
img, video {
width: auto;
height: auto;
max-width: 100%;
cursor: pointer;
opacity: 1;
@ -394,13 +392,13 @@ @@ -394,13 +392,13 @@
width: max-content;
}
img:not(.emoji), video {
/* object-fit: contain; */
object-fit: cover;
width: 100%;
height: 100%;
}
}
img:not(.emoji), video {
/* object-fit: contain; */
object-fit: cover;
width: 100%;
height: 100%;
}
&.is-album {

9
src/scss/partials/_emojiDropdown.scss

@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
.category-items {
display: grid;
grid-column-gap: 2.44px;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(9, 1fr);
font-size: 2.25rem;
line-height: 2.25rem;
@ -121,10 +121,9 @@ @@ -121,10 +121,9 @@
.category-items {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-column-gap: 1px;
> div {
width: 80px;

17
src/scss/partials/_rightSIdebar.scss

@ -1,20 +1,13 @@ @@ -1,20 +1,13 @@
#column-right {
width: 0%;
/* grid-column: 3; */
position: relative;
transition: .2s ease-in-out;
.profile-container {
> .scrollable {
min-width: 25vw;
display: flex;
flex-direction: column;
}
.sidebar-content {
min-width: 25vw;
@media (min-width: $large-screen) {
> .scrollable {
min-width: calc(#{$large-screen} / 4 - 1px);
}
min-width: calc(#{$large-screen} / 4 - 1px);
}
}
@ -51,7 +44,7 @@ @@ -51,7 +44,7 @@
flex: 1 1 auto;
display: flex;
flex-direction: column;
height: 100%;
/* height: 100%; */
position: relative;
width: 100%;
@ -172,7 +165,7 @@ @@ -172,7 +165,7 @@
&-content {
//min-height: 100%;
min-height: calc(100% - 49px);
position: absolute; // FIX THE SAFARI!
//position: absolute; // FIX THE SAFARI!
//position: relative;
/* width: 500%;
margin-left: -100%;

8
src/scss/partials/_scrollable.scss

@ -34,8 +34,8 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -34,8 +34,8 @@ div.scrollable::-webkit-scrollbar-thumb {
left: 0px;
bottom: 0px;
right: 0px;
display: flex;
flex-direction: column;
/* display: flex;
flex-direction: column; */
&.scrollable-x {
overflow-x: auto;
@ -50,7 +50,7 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -50,7 +50,7 @@ div.scrollable::-webkit-scrollbar-thumb {
-ms-overflow-style: none;
}
&-sentinel {
/* &-sentinel {
position: relative;
left: 0;
height: 1px;
@ -58,7 +58,7 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -58,7 +58,7 @@ div.scrollable::-webkit-scrollbar-thumb {
background-color: transparent;
width: 1px;
min-width: 1px;
}
} */
/* &.scrollable-x ~ .scrollbar-thumb {
top: auto;

1
src/scss/partials/popups/_mediaAttacher.scss

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
#{$parent}-photo {
max-height: 320px;
margin: 0 auto;
padding-bottom: 8px;
img {
object-fit: contain;

34
src/scss/style.scss

@ -418,18 +418,11 @@ avatar-element { @@ -418,18 +418,11 @@ avatar-element {
@keyframes ripple-effect {
0% {
//transform: translate(-50%, -50%) scale(0);
transform: scale(0);
}
/* 50% {
opacity: 1;
} */
to {
transform: scale(2);
//transform: translate(-50%, -50%) scale(2);
//opacity: 0;
}
}
@ -1062,30 +1055,6 @@ input:focus, button:focus { @@ -1062,30 +1055,6 @@ input:focus, button:focus {
}
}
/* button {
position: relative;
overflow: hidden;
&:hover {
&:before {
display: block;
}
}
&:before {
display: none;
content: " ";
position: absolute;
z-index: 2;
background: #000;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: .08;
}
} */
.btn-primary {
background: $color-blue;
color: #fff;
@ -1106,9 +1075,8 @@ input:focus, button:focus { @@ -1106,9 +1075,8 @@ input:focus, button:focus {
svg, use {
height: calc(100% - 20px);
right: 12.5px;
right: 15px;
left: auto;
margin: 4px 0 auto;
}
}

Loading…
Cancel
Save