Browse Source

A lot of changes:

Right sidebar animation
Fix animations speed with translate3d
Folders tabs scroll
Fix ripple animation
Right sidebar translateZ blink fix
Misc
master
morethanwords 4 years ago
parent
commit
730e442a7f
  1. 3
      package.json
  2. 1
      rlottie_with_printf/rlottie-wasm.js
  3. BIN
      rlottie_with_printf/rlottie-wasm.wasm
  4. 4
      src/components/appMediaPlaybackController.ts
  5. 2
      src/components/appSelectPeers.ts
  6. 5
      src/components/audio.ts
  7. 16
      src/components/chat/audio.ts
  8. 4
      src/components/emoticonsDropdown/index.ts
  9. 2
      src/components/emoticonsDropdown/tabs/emoji.ts
  10. 2
      src/components/emoticonsDropdown/tabs/gifs.ts
  11. 6
      src/components/emoticonsDropdown/tabs/stickers.ts
  12. 2
      src/components/gifsMasonry.ts
  13. 20
      src/components/horizontalMenu.ts
  14. 4
      src/components/misc.ts
  15. 3
      src/components/poll.ts
  16. 4
      src/components/popupCreatePoll.ts
  17. 2
      src/components/popupStickers.ts
  18. 2
      src/components/preloader.ts
  19. 10
      src/components/ripple.ts
  20. 254
      src/components/scrollable_new.ts
  21. 2
      src/components/sidebarLeft/editProfile.ts
  22. 2
      src/components/sidebarRight/gifs.ts
  23. 2
      src/components/sidebarRight/pollResults.ts
  24. 2
      src/components/sidebarRight/stickers.ts
  25. 13
      src/components/wrappers.ts
  26. 63
      src/helpers/cancellablePromise.ts
  27. 48
      src/helpers/eventListenerBase.ts
  28. 105
      src/helpers/mediaSizes.ts
  29. 61
      src/index.hbs
  30. 13
      src/lib/appManagers/appDialogsManager.ts
  31. 2
      src/lib/appManagers/appDownloadManager.ts
  32. 29
      src/lib/appManagers/appImManager.ts
  33. 16
      src/lib/appManagers/appMediaViewer.ts
  34. 6
      src/lib/appManagers/appMessagesManager.ts
  35. 2
      src/lib/appManagers/appPhotosManager.ts
  36. 8
      src/lib/appManagers/appSidebarLeft.ts
  37. 67
      src/lib/appManagers/appSidebarRight.ts
  38. 2
      src/lib/bin_utils.ts
  39. 73
      src/lib/config.ts
  40. 2
      src/lib/crypto/crypto_methods.ts
  41. 13
      src/lib/crypto/cryptoworker.ts
  42. 368
      src/lib/idb.ts
  43. 5
      src/lib/lottie.ts
  44. 66
      src/lib/lottieLoader.ts
  45. 2
      src/lib/mtproto/apiFileManager.ts
  46. 4
      src/lib/mtproto/mtproto.service.ts
  47. 5
      src/lib/mtproto/mtproto.worker.ts
  48. 4
      src/lib/opusDecodeController.ts
  49. 64
      src/lib/polyfill.ts
  50. 2
      src/lib/webp/webpWorkerController.ts
  51. 2
      src/pages/pageAuthCode.ts
  52. 2
      src/pages/pagePassword.ts
  53. 2
      src/scss/components/_global.scss
  54. 82
      src/scss/partials/_audio.scss
  55. 59
      src/scss/partials/_avatar.scss
  56. 258
      src/scss/partials/_chat.scss
  57. 31
      src/scss/partials/_chatBubble.scss
  58. 24
      src/scss/partials/_chatlist.scss
  59. 12
      src/scss/partials/_ckin.scss
  60. 155
      src/scss/partials/_document.scss
  61. 7
      src/scss/partials/_emojiDropdown.scss
  62. 75
      src/scss/partials/_leftSidebar.scss
  63. 2
      src/scss/partials/_mediaViewer.scss
  64. 40
      src/scss/partials/_rightSidebar.scss
  65. 79
      src/scss/partials/_ripple.scss
  66. 16
      src/scss/partials/_scrollable.scss
  67. 11
      src/scss/partials/_selector.scss
  68. 14
      src/scss/partials/_slider.scss
  69. 9
      src/scss/partials/pages/_chats.scss
  70. 5
      src/scss/partials/pages/_pages.scss
  71. 4
      src/scss/partials/popups/_popup.scss
  72. 511
      src/scss/style.scss
  73. 4
      src/types.d.ts
  74. 462
      src/vendor/smoothscroll.ts
  75. 0
      src/vendor/smoothscroll_minified.js
  76. 46763
      stats.json
  77. BIN
      whybundled.txt

3
package.json

@ -11,7 +11,8 @@ @@ -11,7 +11,8 @@
"build:dev": "webpack --config webpack.dev.js",
"test": "jest --config=jest.config.js",
"profile": "webpack --profile --json > stats.json --config webpack.prod.js",
"profile:dev": "webpack --profile --json > stats.json --config webpack.dev.js"
"profile:dev": "webpack --profile --json > stats.json --config webpack.dev.js",
"whybundled": "npm run profile; whybundled stats.json"
},
"author": "",
"license": "ISC",

1
rlottie_with_printf/rlottie-wasm.js

File diff suppressed because one or more lines are too long

BIN
rlottie_with_printf/rlottie-wasm.wasm

Binary file not shown.

4
src/components/appMediaPlaybackController.ts

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
import { $rootScope } from "../lib/utils";
import appMessagesManager from "../lib/appManagers/appMessagesManager";
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { isSafari } from "../lib/config";
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
import { isSafari } from "../helpers/userAgent";
// TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда

2
src/components/appSelectPeers.ts

@ -307,7 +307,7 @@ export class AppSelectPeers { @@ -307,7 +307,7 @@ export class AppSelectPeers {
this.selectedContainer.insertBefore(div, this.input);
//this.selectedScrollable.scrollTop = this.selectedScrollable.scrollHeight;
this.selectedScrollable.scrollTo(this.selectedScrollable.scrollHeight, true, true);
this.selectedScrollable.scrollTo(this.selectedScrollable.scrollHeight, 'top', true, true);
this.onChange && this.onChange(this.selected.size);
return div;

5
src/components/audio.ts

@ -5,8 +5,9 @@ import ProgressivePreloader from "./preloader"; @@ -5,8 +5,9 @@ import ProgressivePreloader from "./preloader";
import { MediaProgressLine } from "../lib/mediaPlayer";
import appMediaPlaybackController from "./appMediaPlaybackController";
import { DocumentAttribute } from "../layer";
import { mediaSizes, isSafari } from "../lib/config";
import { Download } from "../lib/appManagers/appDownloadManager";
import mediaSizes from "../helpers/mediaSizes";
import { isSafari } from "../helpers/userAgent";
// https://github.com/LonamiWebs/Telethon/blob/4393ec0b83d511b6a20d8a20334138730f084375/telethon/utils.py#L1285
export function decodeWaveform(waveform: Uint8Array | number[]) {
@ -121,7 +122,7 @@ function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement) { @@ -121,7 +122,7 @@ function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement) {
let start = () => {
clearInterval(interval);
interval = setInterval(() => {
interval = window.setInterval(() => {
if(lastIndex > svg.childElementCount || isNaN(audio.duration) || audio.paused) {
clearInterval(interval);
return;

16
src/components/chat/audio.ts

@ -34,11 +34,16 @@ export class ChatAudio { @@ -34,11 +34,16 @@ export class ChatAudio {
this.close.addEventListener('click', (e) => {
cancelEvent(e);
const scrollTop = appImManager.scrollable.scrollTop;
this.container.style.display = 'none';
this.container.parentElement.classList.remove('is-audio-shown');
appImManager.topbar.classList.remove('is-audio-shown');
if(this.toggle.classList.contains('flip-icon')) {
appMediaPlaybackController.toggle();
}
if(!appImManager.topbar.classList.contains('is-pinned-shown')) {
appImManager.scrollable.scrollTop = scrollTop - height;
}
});
this.toggle.addEventListener('click', (e) => {
@ -46,6 +51,8 @@ export class ChatAudio { @@ -46,6 +51,8 @@ export class ChatAudio {
appMediaPlaybackController.toggle();
});
const height = 52;
$rootScope.$on('audio_play', (e) => {
const {doc, mid} = e.detail;
@ -68,8 +75,11 @@ export class ChatAudio { @@ -68,8 +75,11 @@ export class ChatAudio {
if(this.container.style.display) {
const scrollTop = appImManager.scrollable.scrollTop;
this.container.style.display = '';
this.container.parentElement.classList.add('is-audio-shown');
appImManager.scrollable.scrollTop = scrollTop;
appImManager.topbar.classList.add('is-audio-shown');
if(!appImManager.topbar.classList.contains('is-pinned-shown')) {
appImManager.scrollable.scrollTop = scrollTop + height;
}
}
});

4
src/components/emoticonsDropdown/index.ts

@ -6,7 +6,7 @@ import { horizontalMenu } from "../horizontalMenu"; @@ -6,7 +6,7 @@ import { horizontalMenu } from "../horizontalMenu";
import animationIntersector from "../animationIntersector";
import appSidebarRight from "../../lib/appManagers/appSidebarRight";
import appImManager from "../../lib/appManagers/appImManager";
import Scrollable from "../scrollable_new";
import Scrollable, { ScrollableX } from "../scrollable_new";
import EmojiTab from "./tabs/emoji";
import StickersTab from "./tabs/stickers";
import StickyIntersector from "../stickyIntersector";
@ -240,7 +240,7 @@ export class EmoticonsDropdown { @@ -240,7 +240,7 @@ export class EmoticonsDropdown {
//animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
};
public static menuOnClick = (menu: HTMLUListElement, scroll: Scrollable, menuScroll?: Scrollable) => {
public static menuOnClick = (menu: HTMLUListElement, scroll: Scrollable, menuScroll?: ScrollableX) => {
let prevId = 0;
let jumpedTo = -1;

2
src/components/emoticonsDropdown/tabs/emoji.ts

@ -81,7 +81,7 @@ export default class EmojiTab implements EmoticonsTab { @@ -81,7 +81,7 @@ export default class EmojiTab implements EmoticonsTab {
//console.timeEnd('emojiParse');
const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement;
const emojiScroll = this.scroll = new Scrollable(this.content, 'y', 'EMOJI', null);
const emojiScroll = this.scroll = new Scrollable(this.content, 'EMOJI', null);
//emojiScroll.setVirtualContainer(emojiScroll.container);

2
src/components/emoticonsDropdown/tabs/gifs.ts

@ -13,7 +13,7 @@ export default class GifsTab implements EmoticonsTab { @@ -13,7 +13,7 @@ export default class GifsTab implements EmoticonsTab {
const gifsContainer = this.content.firstElementChild as HTMLDivElement;
gifsContainer.addEventListener('click', EmoticonsDropdown.onMediaClick);
const scroll = new Scrollable(this.content, 'y', 'GIFS', null);
const scroll = new Scrollable(this.content, 'GIFS', null);
const masonry = new GifsMasonry(gifsContainer, EMOTICONSSTICKERGROUP, scroll);
const preloader = putPreloader(this.content, true);

6
src/components/emoticonsDropdown/tabs/stickers.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import emoticonsDropdown, { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from "..";
import { StickerSet } from "../../../layer";
import Scrollable from "../../scrollable_new";
import Scrollable, { ScrollableX } from "../../scrollable_new";
import { wrapSticker } from "../../wrappers";
import appStickersManager from "../../../lib/appManagers/appStickersManager";
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
@ -230,7 +230,7 @@ export default class StickersTab implements EmoticonsTab { @@ -230,7 +230,7 @@ export default class StickersTab implements EmoticonsTab {
let menuWrapper = this.content.previousElementSibling as HTMLDivElement;
this.menu = menuWrapper.firstElementChild.firstElementChild as HTMLUListElement;
let menuScroll = new Scrollable(menuWrapper, 'x');
let menuScroll = new ScrollableX(menuWrapper);
let stickersDiv = document.createElement('div');
stickersDiv.classList.add('stickers-categories');
@ -274,7 +274,7 @@ export default class StickersTab implements EmoticonsTab { @@ -274,7 +274,7 @@ export default class StickersTab implements EmoticonsTab {
stickersDiv.addEventListener('click', EmoticonsDropdown.onMediaClick);
this.scroll = new Scrollable(this.content, 'y', 'STICKERS', undefined, undefined, 2);
this.scroll = new Scrollable(this.content, 'STICKERS', undefined, undefined, 2);
this.scroll.setVirtualContainer(stickersDiv);
this.stickyIntersector = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll);

2
src/components/gifsMasonry.ts

@ -3,9 +3,9 @@ import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager"; @@ -3,9 +3,9 @@ import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { wrapVideo } from "./wrappers";
import { renderImageFromUrl } from "./misc";
import { LazyLoadQueueRepeat2 } from "./lazyLoadQueue";
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
import animationIntersector from "./animationIntersector";
import Scrollable from "./scrollable_new";
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
const width = 400;
const maxSingleWidth = width - 100;

20
src/components/horizontalMenu.ts

@ -10,14 +10,15 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t @@ -10,14 +10,15 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t
tabContent.style.transform = `translateX(-25%)`;
prevTabContent.style.transform = `translateX(20%)`;
} */
const width = prevTabContent.getBoundingClientRect().width;
if(toRight) {
prevTabContent.style.filter = `brightness(80%)`;
prevTabContent.style.transform = `translateX(-25%)`;
tabContent.style.transform = `translateX(100%)`;
prevTabContent.style.transform = `translate3d(${-width * .25}px, 0, 0)`;
tabContent.style.transform = `translate3d(${width}px, 0, 0)`;
} else {
tabContent.style.filter = `brightness(80%)`;
tabContent.style.transform = `translateX(-25%)`;
prevTabContent.style.transform = `translateX(100%)`;
tabContent.style.transform = `translate3d(${-width * .25}px, 0, 0)`;
prevTabContent.style.transform = `translate3d(${width}px, 0, 0)`;
}
tabContent.classList.add('active');
@ -28,12 +29,13 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t @@ -28,12 +29,13 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t
}
function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
const width = prevTabContent.getBoundingClientRect().width;
if(toRight) {
tabContent.style.transform = `translateX(100%)`;
prevTabContent.style.transform = `translateX(-100%)`;
tabContent.style.transform = `translate3d(${width}px, 0, 0)`;
prevTabContent.style.transform = `translate3d(${-width}px, 0, 0)`;
} else {
tabContent.style.transform = `translateX(-100%)`;
prevTabContent.style.transform = `translateX(100%)`;
tabContent.style.transform = `translate3d(${-width}px, 0, 0)`;
prevTabContent.style.transform = `translate3d(${width}px, 0, 0)`;
}
tabContent.classList.add('active');
@ -83,7 +85,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick? @@ -83,7 +85,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
const _prevId = prevId;
if(hideTimeouts.hasOwnProperty(id)) clearTimeout(hideTimeouts[id]);
if(p/* && false */) {
hideTimeouts[_prevId] = setTimeout(() => {
hideTimeouts[_prevId] = window.setTimeout(() => {
p.style.transform = '';
p.style.filter = '';
p.classList.remove('active');

4
src/components/misc.ts

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
import Config, { touchSupport, isApple, mediaSizes } from "../lib/config";
import mediaSizes from "../helpers/mediaSizes";
import { isApple } from "../helpers/userAgent";
import Config, { touchSupport } from "../lib/config";
export const loadedURLs: {[url: string]: boolean} = {};
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {

3
src/components/poll.ts

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
import appPollsManager, { PollResults, Poll } from "../lib/appManagers/appPollsManager";
import { RichTextProcessor } from "../lib/richtextprocessor";
import { findUpClassName, $rootScope, cancelEvent } from "../lib/utils";
import { mediaSizes, touchSupport } from "../lib/config";
import { touchSupport } from "../lib/config";
import appSidebarRight from "../lib/appManagers/appSidebarRight";
import appImManager from "../lib/appManagers/appImManager";
import serverTimeManager from "../lib/mtproto/serverTimeManager";
import { ripple } from "./ripple";
import mediaSizes from "../helpers/mediaSizes";
let lineTotalLength = 0;
const tailLength = 9;

4
src/components/popupCreatePoll.ts

@ -46,7 +46,7 @@ export default class PopupCreatePoll extends PopupElement { @@ -46,7 +46,7 @@ export default class PopupCreatePoll extends PopupElement {
this.confirmBtn.addEventListener('click', this.onSubmitClick);
this.scrollable = new Scrollable(this.body, 'y', undefined);
this.scrollable = new Scrollable(this.body);
this.appendMoreField();
}
@ -131,6 +131,6 @@ export default class PopupCreatePoll extends PopupElement { @@ -131,6 +131,6 @@ export default class PopupCreatePoll extends PopupElement {
this.questions.append(questionField);
this.scrollable.scrollTo(this.scrollable.scrollHeight, true, true);
this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true);
}
}

2
src/components/popupStickers.ts

@ -64,7 +64,7 @@ export default class PopupStickers extends PopupElement { @@ -64,7 +64,7 @@ export default class PopupStickers extends PopupElement {
this.stickersFooter.innerText = 'Loading...';
this.body.append(div);
const scrollable = new Scrollable(this.body, 'y', undefined);
const scrollable = new Scrollable(this.body);
this.body.append(this.stickersFooter);
// const editButton = document.createElement('button');

2
src/components/preloader.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { isInDOM, cancelEvent } from "../lib/utils";
import { CancellablePromise } from "../lib/polyfill";
import { CancellablePromise } from "../helpers/cancellablePromise";
export default class ProgressivePreloader {
public preloader: HTMLDivElement;

10
src/components/ripple.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
import { touchSupport } from "../lib/config";
import { findUpClassName } from "../lib/utils";
let rippleClickID = 0;
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
@ -28,6 +29,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool @@ -28,6 +29,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
//console.log('ripple drawRipple');
handler = () => {
//return;
let elapsedTime = Date.now() - startTime;
if(elapsedTime < duration) {
let delay = Math.max(duration - elapsedTime, duration / 2);
@ -66,8 +68,8 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool @@ -66,8 +68,8 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
} */
window.requestAnimationFrame(() => {
span.classList.add('c-ripple__circle');
let rect = r.getBoundingClientRect();
span.classList.add('c-ripple__circle');
let clickX = clientX - rect.left;
let clickY = clientY - rect.top;
@ -110,7 +112,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool @@ -110,7 +112,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
elem.addEventListener('touchstart', (e) => {
//console.log('ripple touchstart', e);
if(e.touches.length > 1 || ((e.target as HTMLElement).tagName == 'BUTTON' && e.target != elem)) {
if(e.touches.length > 1 || ((e.target as HTMLElement).tagName == 'BUTTON' && e.target != elem) || findUpClassName(e.target as HTMLElement, 'c-ripple') != r) {
return;
}
@ -130,7 +132,9 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool @@ -130,7 +132,9 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
}, {passive: true});
} else {
elem.addEventListener('mousedown', (e) => {
if(elem.dataset.ripple == '0') {
//console.log('ripple mousedown', e, e.target, findUpClassName(e.target as HTMLElement, 'c-ripple') == r);
if(elem.dataset.ripple == '0' || findUpClassName(e.target as HTMLElement, 'c-ripple') != r) {
return false;
} else if(touchStartFired) {
touchStartFired = false;

254
src/components/scrollable_new.ts

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
import { logger, LogLevels } from "../lib/logger";
import smoothscroll from '../vendor/smoothscroll';
import { touchSupport, isSafari, mediaSizes } from "../lib/config";
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
import smoothscroll, { SCROLL_TIME, SmoothScrollToOptions } from '../vendor/smoothscroll';
import { touchSupport } from "../lib/config";
//import { CancellablePromise, deferredPromise } from "../lib/polyfill";
//import { isInDOM } from "../lib/utils";
(window as any).__forceSmoothScrollPolyfill__ = true;
smoothscroll.polyfill();
smoothscroll();
/*
var el = $0;
var height = 0;
@ -48,10 +48,88 @@ const scrollsIntersector = new IntersectionObserver(entries => { @@ -48,10 +48,88 @@ const scrollsIntersector = new IntersectionObserver(entries => {
}
}); */
export default class Scrollable {
//public container: HTMLDivElement;
public overflowContainer: HTMLElement;
export class ScrollableBase {
protected log: ReturnType<typeof logger>;
protected onScroll: () => void;
public getScrollValue: () => number;
public scrollLocked = 0;
constructor(public el: HTMLElement, logPrefix = '', public appendTo = el, public container: HTMLElement = document.createElement('div')) {
this.container.classList.add('scrollable');
if(!appendTo) {
this.appendTo = this.container;
}
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''), LogLevels.error);
if(el) {
Array.from(el.children).forEach(c => this.container.append(c));
el.append(this.container);
}
//this.onScroll();
}
protected setListeners() {
window.addEventListener('resize', this.onScroll);
this.container.addEventListener('scroll', this.onScroll, {passive: true, capture: true});
}
public prepend(element: HTMLElement) {
this.appendTo.prepend(element);
}
public append(element: HTMLElement) {
this.appendTo.append(element);
}
public contains(element: Element) {
return !!element.parentElement;
}
public removeElement(element: Element) {
element.remove();
}
public scrollTo(value: number, side: 'top' | 'left', smooth = true, important = false, scrollTime = SCROLL_TIME) {
if(this.scrollLocked && !important) return;
const scrollValue = this.getScrollValue();
if(scrollValue == Math.floor(value)) {
return;
}
if(this.scrollLocked) clearTimeout(this.scrollLocked);
/* else {
this.scrollLockedPromise = deferredPromise<void>();
} */
this.scrollLocked = window.setTimeout(() => {
this.scrollLocked = 0;
//this.scrollLockedPromise.resolve();
//this.onScroll();
this.container.dispatchEvent(new CustomEvent('scroll'));
}, scrollTime);
const options: SmoothScrollToOptions = {
behavior: smooth ? 'smooth' : 'auto',
scrollTime
};
options[side] = value;
this.container.scrollTo(options as any);
}
get length() {
return this.appendTo.childElementCount;
}
}
export default class Scrollable extends ScrollableBase {
public splitUp: HTMLElement;
public onScrolledTop: () => void = null;
@ -63,8 +141,6 @@ export default class Scrollable { @@ -63,8 +141,6 @@ export default class Scrollable {
private disableHoverTimeout: number = 0;
private log: ReturnType<typeof logger>;
/* private sentinelsObserver: IntersectionObserver;
private topSentinel: HTMLDivElement;
private bottomSentinel: HTMLDivElement; */
@ -80,8 +156,7 @@ export default class Scrollable { @@ -80,8 +156,7 @@ export default class Scrollable {
/* private onScrolledTopFired = false;
private onScrolledBottomFired = false; */
public scrollLocked = 0;
public scrollLockedPromise: CancellablePromise<void> = Promise.resolve();
//public scrollLockedPromise: CancellablePromise<void> = Promise.resolve();
public isVisible = false;
private reorderTimeout: number;
@ -102,8 +177,8 @@ export default class Scrollable { @@ -102,8 +177,8 @@ export default class Scrollable {
this.visible.delete(element);
}
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', logPrefix = '', public appendTo = el, public onScrollOffset = 300, public splitCount = 15, public container: HTMLElement = document.createElement('div')) {
this.container.classList.add('scrollable');
constructor(el: HTMLElement, logPrefix = '', appendTo = el, public onScrollOffset = 300, public splitCount = 15, container: HTMLElement = document.createElement('div')) {
super(el, logPrefix, appendTo, container);
this.visible = new Set();
this.observer = new IntersectionObserver(entries => {
@ -173,63 +248,11 @@ export default class Scrollable { @@ -173,63 +248,11 @@ export default class Scrollable {
}
});
if(!appendTo) {
this.appendTo = this.container;
}
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''), LogLevels.error);
if(axis == 'x') {
this.container.classList.add('scrollable-x');
if(!touchSupport) {
const scrollHorizontally = (e: any) => {
e = window.event || e;
if(e.which == 1) {
// maybe horizontal scroll is natively supports, works on macbook
return;
}
const 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(axis == 'y') {
this.container.classList.add('scrollable-y');
} else {
throw new Error('no side for scroll');
}
window.addEventListener('resize', () => {
this.overflowContainer = mediaSizes.isMobile && false ? document.documentElement : this.container;
this.onScroll();
});
this.container.addEventListener('scroll', this.onScroll, {passive: true, capture: true});
//document.documentElement.addEventListener('scroll', binded, {passive: true, capture: true});
//window.addEventListener('scroll', binded, {passive: true, capture: true});
if(el) {
Array.from(el.children).forEach(c => this.container.append(c));
this.container.classList.add('scrollable-y');
el.append(this.container);
}
//this.onScroll();
this.overflowContainer = mediaSizes.isMobile && false ? document.documentElement : this.container;
/* scrollables.set(this.container, this);
scrollsIntersector.observe(this.container); */
this.setListeners();
}
// public attachSentinels(container = this.container, offset = this.onScrollOffset) {
@ -329,12 +352,12 @@ export default class Scrollable { @@ -329,12 +352,12 @@ export default class Scrollable {
this.onScrollMeasure = window.requestAnimationFrame(() => {
//if(!this.isVisible) return;
this.checkForTriggers(this.overflowContainer);
this.checkForTriggers();
this.onScrollMeasure = 0;
if(!this.splitUp) return;
const scrollTop = this.overflowContainer.scrollTop;
const scrollTop = this.scrollTop;
if(this.lastScrollTop != scrollTop) {
this.lastScrollDirection = this.lastScrollTop < scrollTop ? 1 : -1;
this.lastScrollTop = scrollTop;
@ -344,9 +367,10 @@ export default class Scrollable { @@ -344,9 +367,10 @@ export default class Scrollable {
});
};
public checkForTriggers(container: HTMLElement) {
public checkForTriggers() {
if(this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) return;
const container = this.container;
const scrollHeight = container.scrollHeight;
if(!scrollHeight) { // незачем вызывать триггеры если блок пустой или не виден
return;
@ -439,11 +463,11 @@ export default class Scrollable { @@ -439,11 +463,11 @@ export default class Scrollable {
if(element.parentElement && !this.scrollLocked) {
const isFirstUnread = element.classList.contains('is-first-unread');
let offsetTop = element.getBoundingClientRect().top - this.container.getBoundingClientRect().top;
offsetTop = this.container.scrollTop + offsetTop;
let offset = element.getBoundingClientRect().top - this.container.getBoundingClientRect().top;
offset = this.scrollTop + offset;
if(!smooth && isFirstUnread) {
this.scrollTo(offsetTop, false);
this.scrollTo(offset, 'top', false);
return;
}
@ -451,39 +475,20 @@ export default class Scrollable { @@ -451,39 +475,20 @@ export default class Scrollable {
const height = element.scrollHeight;
const d = (clientHeight - height) / 2;
offsetTop -= d;
offset -= d;
this.scrollTo(offsetTop, smooth);
}
}
public scrollTo(top: number, smooth = true, important = false) {
if(this.scrollLocked && !important) return;
const scrollTop = this.scrollTop;
if(scrollTop == Math.floor(top)) {
return;
}
if(this.scrollLocked) clearTimeout(this.scrollLocked);
else {
this.scrollLockedPromise = deferredPromise<void>();
this.scrollTo(offset, 'top', smooth);
}
this.scrollLocked = window.setTimeout(() => {
this.scrollLocked = 0;
this.scrollLockedPromise.resolve();
//this.onScroll();
this.container.dispatchEvent(new CustomEvent('scroll'));
}, 468);
this.container.scrollTo({behavior: smooth ? 'smooth' : 'auto', top});
}
public removeElement(element: Element) {
element.remove();
}
public getScrollValue = () => {
return this.scrollTop;
};
set scrollTop(y: number) {
this.container.scrollTop = y;
}
@ -496,8 +501,57 @@ export default class Scrollable { @@ -496,8 +501,57 @@ export default class Scrollable {
get scrollHeight() {
return this.container.scrollHeight;
}
}
get length() {
return this.appendTo.childElementCount;
export class ScrollableX extends ScrollableBase {
constructor(public el: HTMLElement, logPrefix = '', public appendTo = el, public onScrollOffset = 300, public splitCount = 15, public container: HTMLElement = document.createElement('div')) {
super(el, logPrefix, appendTo, container);
this.container.classList.add('scrollable-x');
if(!touchSupport) {
const scrollHorizontally = (e: any) => {
e = window.event || e;
if(e.which == 1) {
// maybe horizontal scroll is natively supports, works on macbook
return;
}
const 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);
}
}
this.setListeners();
}
public scrollIntoView(element: HTMLElement, smooth = true, scrollTime?: number) {
if(element.parentElement && !this.scrollLocked) {
let offset = element.getBoundingClientRect().left - this.container.getBoundingClientRect().left;
offset = this.getScrollValue() + offset;
const clientWidth = this.container.clientWidth;
const width = element.scrollWidth;
const d = (clientWidth - width) / 2;
offset -= d;
this.scrollTo(offset, 'left', smooth, undefined, scrollTime);
}
}
public getScrollValue = () => {
return this.container.scrollLeft;
};
}

2
src/components/sidebarLeft/editProfile.ts

@ -134,7 +134,7 @@ export default class AppEditProfileTab implements SliderTab { @@ -134,7 +134,7 @@ export default class AppEditProfileTab implements SliderTab {
});
});
let scrollable = new Scrollable(this.scrollWrapper as HTMLElement, 'y');
let scrollable = new Scrollable(this.scrollWrapper as HTMLElement);
}
public fillElements() {

2
src/components/sidebarRight/gifs.ts

@ -31,7 +31,7 @@ export default class AppGifsTab implements SliderTab { @@ -31,7 +31,7 @@ export default class AppGifsTab implements SliderTab {
private searchPromise: ReturnType<AppInlineBotsManager['getInlineResults']>;
constructor() {
this.scrollable = new Scrollable(this.contentDiv, 'y', ANIMATIONGROUP, undefined, undefined, 2);
this.scrollable = new Scrollable(this.contentDiv, ANIMATIONGROUP, undefined, undefined, 2);
this.scrollable.setVirtualContainer(this.gifsDiv);
this.masonry = new GifsMasonry(this.gifsDiv, ANIMATIONGROUP, this.scrollable);

2
src/components/sidebarRight/pollResults.ts

@ -17,7 +17,7 @@ export default class AppPollResultsTab implements SliderTab { @@ -17,7 +17,7 @@ export default class AppPollResultsTab implements SliderTab {
private mid: number;
constructor() {
this.scrollable = new Scrollable(this.contentDiv, 'y', 'POLL-RESULTS', undefined, undefined, 2);
this.scrollable = new Scrollable(this.contentDiv, 'POLL-RESULTS', undefined, undefined, 2);
}
public cleanup() {

2
src/components/sidebarRight/stickers.ts

@ -23,7 +23,7 @@ export default class AppStickersTab implements SliderTab { @@ -23,7 +23,7 @@ export default class AppStickersTab implements SliderTab {
private lazyLoadQueue: LazyLoadQueue;
constructor() {
this.scrollable = new Scrollable(this.contentDiv, 'y', 'STICKERS-SEARCH', undefined, undefined, 2);
this.scrollable = new Scrollable(this.contentDiv, 'STICKERS-SEARCH', undefined, undefined, 2);
this.scrollable.setVirtualContainer(this.setsDiv);
this.lazyLoadQueue = new LazyLoadQueue();

13
src/components/wrappers.ts

@ -10,7 +10,6 @@ import { renderImageFromUrl } from './misc'; @@ -10,7 +10,6 @@ import { renderImageFromUrl } from './misc';
import appMessagesManager from '../lib/appManagers/appMessagesManager';
import { Layouter, RectPart } from './groupedLayout';
import PollElement from './poll';
import { mediaSizes, isSafari } from '../lib/config';
import animationIntersector from './animationIntersector';
import AudioElement from './audio';
import { DownloadBlob } from '../lib/appManagers/appDownloadManager';
@ -18,7 +17,9 @@ import webpWorkerController from '../lib/webp/webpWorkerController'; @@ -18,7 +17,9 @@ import webpWorkerController from '../lib/webp/webpWorkerController';
import { readBlobAsText } from '../helpers/blob';
import appMediaPlaybackController from './appMediaPlaybackController';
import { PhotoSize } from '../layer';
import { deferredPromise } from '../lib/polyfill';
import { deferredPromise } from '../helpers/cancellablePromise';
import mediaSizes from '../helpers/mediaSizes';
import { isSafari } from '../helpers/userAgent';
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
doc: MyDocument,
@ -704,6 +705,14 @@ export function wrapReply(title: string, subtitle: string, message?: any, isPinn @@ -704,6 +705,14 @@ export function wrapReply(title: string, subtitle: string, message?: any, isPinn
const replySubtitle = document.createElement('div');
replySubtitle.classList.add(prefix + '-subtitle');
if(title.length > 150) {
title = title.substr(0, 140) + '...';
}
if(subtitle.length > 150) {
subtitle = subtitle.substr(0, 140) + '...';
}
replyTitle.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : '';

63
src/helpers/cancellablePromise.ts

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
export interface CancellablePromise<T> extends Promise<T> {
resolve?: (...args: any[]) => void,
reject?: (...args: any[]) => void,
cancel?: () => void,
notify?: (...args: any[]) => void,
notifyAll?: (...args: any[]) => void,
lastNotify?: any,
listeners?: Array<(...args: any[]) => void>,
addNotifyListener?: (callback: (...args: any[]) => void) => void,
isFulfilled?: boolean,
isRejected?: boolean
}
export function deferredPromise<T>() {
let deferredHelper: any = {
isFulfilled: false,
isRejected: false,
notify: () => {},
notifyAll: (...args: any[]) => {
deferredHelper.lastNotify = args;
deferredHelper.listeners.forEach((callback: any) => callback(...args));
},
lastNotify: undefined,
listeners: [],
addNotifyListener: (callback: (...args: any[]) => void) => {
if(deferredHelper.lastNotify) {
callback(...deferredHelper.lastNotify);
}
deferredHelper.listeners.push(callback);
}
};
let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => {
deferredHelper.resolve = (value: T) => {
if(deferred.isFulfilled) return;
deferred.isFulfilled = true;
resolve(value);
};
deferredHelper.reject = (...args: any[]) => {
if(deferred.isRejected) return;
deferred.isRejected = true;
reject(...args);
};
});
deferred.finally(() => {
deferred.notify = null;
deferred.listeners.length = 0;
deferred.lastNotify = null;
});
Object.assign(deferred, deferredHelper);
return deferred;
}

48
src/helpers/eventListenerBase.ts

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
import type { ArgumentTypes } from "../types";
export default class EventListenerBase<Listeners extends {[name: string]: Function}> {
protected listeners: Partial<{
[k in keyof Listeners]: Array<{callback: Listeners[k], once?: true}>
}> = {};
protected listenerResults: Partial<{
[k in keyof Listeners]: ArgumentTypes<Listeners[k]>
}> = {};
constructor(private reuseResults?: true) {
}
public addListener(name: keyof Listeners, callback: Listeners[typeof name], once?: true) {
(this.listeners[name] ?? (this.listeners[name] = [])).push({callback, once});
if(this.listenerResults.hasOwnProperty(name)) {
callback(this.listenerResults[name]);
if(once) {
this.removeListener(name, callback);
}
}
}
public removeListener(name: keyof Listeners, callback: Listeners[typeof name]) {
if(this.listeners[name]) {
this.listeners[name].findAndSplice(l => l.callback == callback);
}
}
protected setListenerResult(name: keyof Listeners, ...args: ArgumentTypes<Listeners[typeof name]>) {
if(this.reuseResults) {
this.listenerResults[name] = args;
}
if(this.listeners[name]) {
this.listeners[name].forEach(listener => {
listener.callback(...args);
if(listener.once) {
this.removeListener(name, listener.callback);
}
});
}
}
}

105
src/helpers/mediaSizes.ts

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
import EventListenerBase from "./eventListenerBase";
type Size = {width: number, height: number};
type Sizes = {
regular: Size,
webpage: Size,
album: Size
};
export enum ScreenSize {
mobile,
medium,
large
}
const MOBILE_SIZE = 896;
const MEDIUM_SIZE = 1275;
const LARGE_SIZE = 1680;
class MediaSizes extends EventListenerBase<{
changeScreen: (from: ScreenSize, to: ScreenSize) => void
}> {
private screenSizes: {key: ScreenSize, value: number}[] = [
{key: ScreenSize.mobile, value: MOBILE_SIZE - 1},
{key: ScreenSize.medium, value: MEDIUM_SIZE},
{key: ScreenSize.large, value: LARGE_SIZE}
];
private sizes: {[k in 'desktop' | 'handhelds']: Sizes} = {
handhelds: {
regular: {
width: 293,
height: 293
},
webpage: {
width: 293,
height: 213
},
album: {
width: 293,
height: 0
}
},
desktop: {
regular: {
width: 480,
height: 480
},
webpage: {
width: 480,
height: 400
},
album: {
width: 451,
height: 0
}
}
};
public isMobile = false;
public active: Sizes;
public activeScreen: ScreenSize;
constructor() {
super();
window.addEventListener('resize', this.handleResize);
this.handleResize();
}
private handleResize = () => {
const innerWidth = window.innerWidth;
//this.isMobile = innerWidth <= 720;
let activeScreen = this.screenSizes[0].key;
for(let i = this.screenSizes.length - 1; i >= 0; --i) {
if(this.screenSizes[i].value < innerWidth) {
activeScreen = (this.screenSizes[i + 1] || this.screenSizes[i]).key;
break;
}
}
if(this.activeScreen != activeScreen) {
//console.log('changeScreen', this.activeScreen, activeScreen);
this.setListenerResult('changeScreen', this.activeScreen, activeScreen);
}
this.activeScreen = activeScreen;
this.isMobile = this.activeScreen == ScreenSize.mobile;
this.active = this.isMobile ? this.sizes.handhelds : this.sizes.desktop;
/* if(this.isMobile) {
for(let i in this.active) {
// @ts-ignore
let size = this.active[i];
size.width = innerWidth
}
} */
};
}
const mediaSizes = new MediaSizes();
export default mediaSizes;

61
src/index.hbs

@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
{{/ each }}
</head>
<body>
<body class="animation-level-2">
<!--[if IE]>
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
<![endif]-->
@ -212,7 +212,7 @@ @@ -212,7 +212,7 @@
<div class="sidebar-content">
<div id="chats-container">
<div class="folders-tabs-scrollable hide">
<nav class="menu-horizontal no-stripe" id="folders-tabs">
<nav class="menu-horizontal" id="folders-tabs">
<ul>
<li class="rp"><span>All<span class="unread-count"></span><i></i></span></li>
</ul>
@ -237,7 +237,7 @@ @@ -237,7 +237,7 @@
</div>
<div class="sidebar-slider-item">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title">Archived Chats</div>
</div>
<div class="sidebar-content">
@ -248,7 +248,7 @@ @@ -248,7 +248,7 @@
</div>
<div class="sidebar-slider-item" id="contacts-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
</div>
<div class="sidebar-content">
<div>
@ -258,7 +258,7 @@ @@ -258,7 +258,7 @@
</div>
<div class="sidebar-slider-item new-channel-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title">New Channel</div>
</div>
<div class="sidebar-content">
@ -282,7 +282,7 @@ @@ -282,7 +282,7 @@
</div>
<div class="sidebar-slider-item addmembers-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title">Add Members</div>
</div>
<div class="sidebar-content">
@ -291,7 +291,7 @@ @@ -291,7 +291,7 @@
</div>
<div class="sidebar-slider-item new-group-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title">New Group</div>
</div>
<div class="sidebar-content">
@ -310,7 +310,7 @@ @@ -310,7 +310,7 @@
</div>
<div class="sidebar-slider-item settings-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title">Settings</div>
<div class="btn-icon tgico-more rp btn-menu-toggle">
<div class="btn-menu bottom-left">
@ -336,7 +336,7 @@ @@ -336,7 +336,7 @@
</div>
<div class="sidebar-slider-item edit-profile-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title">Edit Profile</div>
</div>
<div class="sidebar-content">
@ -377,7 +377,7 @@ @@ -377,7 +377,7 @@
</div>
<div class="sidebar-slider-item chat-folders-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title">Chat Folders</div>
</div>
<div class="sidebar-content">
@ -401,7 +401,7 @@ @@ -401,7 +401,7 @@
</div>
<div class="sidebar-slider-item edit-folder-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title"></div>
<div class="btn-icon tgico-check1 btn-confirm rp hide"></div>
<div class="btn-icon btn-menu-toggle rp tgico-more hide">
@ -445,7 +445,7 @@ @@ -445,7 +445,7 @@
</div>
<div class="sidebar-slider-item included-chats-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="sidebar-header__title"></div>
<div class="btn-icon tgico-check1 btn-confirm rp" style="display: none;"></div>
</div>
@ -460,9 +460,10 @@ @@ -460,9 +460,10 @@
</div>
</div>
<div class="chat-container main-column" id="column-center">
<canvas id="chat-background-canvas"></canvas>
{{!-- <canvas id="chat-background-canvas"></canvas> --}}
<div class="chat-background"></div>
<div id="topbar" style="display: none;" class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<button class="btn-icon tgico-back sidebar-close-button"></button>
<div class="chat-info">
<div class="person">
<avatar-element id="im-avatar" dialog="1"></avatar-element>
@ -476,21 +477,23 @@ @@ -476,21 +477,23 @@
</div>
</div>
</div>
<button class="btn-primary rp chat-join hide">SUBSCRIBE</button>
<div class="btn-icon rp chat-mute-button hide"></div>
<div class="btn-icon rp tgico-search chat-search-button"></div>
<div class="btn-icon btn-menu-toggle rp tgico-more chat-more-button">
<div class="btn-menu bottom-left">
<div class="btn-menu-item menu-search tgico-search rp">Search</div>
<div class="btn-menu-item menu-mute rp">Mute</div>
<div class="btn-menu-item menu-delete tgico-delete danger rp btn-disabled">Delete and Leave</div>
<div class="chat-utils">
<button class="btn-primary rp chat-join hide">SUBSCRIBE</button>
<div class="btn-icon rp chat-mute-button hide"></div>
<div class="btn-icon rp tgico-search chat-search-button"></div>
<div class="btn-icon btn-menu-toggle rp tgico-more chat-more-button">
<div class="btn-menu bottom-left">
<div class="btn-menu-item menu-search tgico-search rp">Search</div>
<div class="btn-menu-item menu-mute rp">Mute</div>
<div class="btn-menu-item menu-delete tgico-delete danger rp btn-disabled">Delete and Leave</div>
</div>
</div>
</div>
</div>
<div id="bubbles" class="scrolled-down">
<div id="bubbles-inner"></div>
<div id="bubbles-go-down" class="tgico-down z-depth-1 rp" style="display: none;"></div>
</div>
<div id="bubbles-go-down" class="tgico-down btn-corner z-depth-1 rp hide"></div>
<div class="btn-menu" id="bubble-contextmenu">
<div class="btn-menu-item menu-reply tgico-reply rp">Reply</div>
<div class="btn-menu-item menu-edit tgico-edit rp">Edit</div>
@ -587,7 +590,7 @@ @@ -587,7 +590,7 @@
<div class="sidebar-content sidebar-slider tabs-container">
<div class="sidebar-slider-item profile-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico sidebar-close-button"></button>
<button class="btn-icon tgico sidebar-close-button"></button>
<div class="sidebar-header__title">Info</div>
<!-- <button class="btn-icon rp tgico-edit sidebar-edit-button"></button> -->
@ -648,33 +651,33 @@ @@ -648,33 +651,33 @@
</div>
<div class="sidebar-slider-item sidebar-search chats-container" id="search-private-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
<button class="btn-icon tgico-close sidebar-close-button"></button>
</div>
<div class="chats-container"></div>
</div>
<div class="sidebar-slider-item sidebar-search" id="forward-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
<button class="btn-icon tgico-close sidebar-close-button"></button>
<div class="sidebar-header__title">Forward</div>
</div>
<button class="btn-primary btn-circle btn-icon rp btn-corner tgico-send"></button>
</div>
<div class="sidebar-slider-item sidebar-search chats-container" id="stickers-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
<button class="btn-icon tgico-close sidebar-close-button"></button>
</div>
<div class="sidebar-content"><div class="sticker-sets"></div></div>
</div>
<div class="sidebar-slider-item chats-container" id="poll-results-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
<button class="btn-icon tgico-close sidebar-close-button"></button>
<div class="sidebar-header__title">Results</div>
</div>
<div class="sidebar-content"><div class="poll-results"></div></div>
</div>
<div class="sidebar-slider-item sidebar-search chats-container" id="search-gifs-container">
<div class="sidebar-header">
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
<button class="btn-icon tgico-close sidebar-close-button"></button>
</div>
<div class="sidebar-content"><div class="gifs-masonry"></div></div>
</div>

13
src/lib/appManagers/appDialogsManager.ts

@ -6,16 +6,17 @@ import appUsersManager, { User } from "./appUsersManager"; @@ -6,16 +6,17 @@ import appUsersManager, { User } from "./appUsersManager";
import { RichTextProcessor } from "../richtextprocessor";
import { putPreloader, positionMenu, openBtnMenu, parseMenuButtonsTo, attachContextMenuListener } from "../../components/misc";
//import Scrollable from "../../components/scrollable";
import Scrollable from "../../components/scrollable_new";
import Scrollable, { ScrollableX } from "../../components/scrollable_new";
import { logger, LogLevels } from "../logger";
import appChatsManager from "./appChatsManager";
import AvatarElement from "../../components/avatar";
import { PopupButton, PopupPeer } from "../../components/popup";
import { SliderTab } from "../../components/slider";
import appStateManager from "./appStateManager";
import { touchSupport, isSafari } from "../config";
import { touchSupport } from "../config";
import { horizontalMenu } from "../../components/horizontalMenu";
import { ripple } from "../../components/ripple";
import { isSafari } from "../../helpers/userAgent";
type DialogDom = {
avatarEl: AvatarElement,
@ -275,7 +276,7 @@ export class AppArchivedTab implements SliderTab { @@ -275,7 +276,7 @@ export class AppArchivedTab implements SliderTab {
public wasFilterID: number;
init() {
this.scroll = new Scrollable(this.container, 'y', 'CLA', this.chatList, 500);
this.scroll = new Scrollable(this.container, 'CLA', this.chatList, 500);
this.scroll.setVirtualContainer(this.chatList);
this.scroll.onScrolledBottom = appDialogsManager.onChatsScroll;
///this.scroll.attachSentinels();
@ -379,7 +380,7 @@ export class AppDialogsManager { @@ -379,7 +380,7 @@ export class AppDialogsManager {
this.folders.menuScrollContainer = this.folders.menu.parentElement;
this.scroll = this._scroll = new Scrollable(this.chatsContainer, 'y', 'CL', this.chatList, 500);
this.scroll = this._scroll = new Scrollable(this.chatsContainer, 'CL', this.chatList, 500);
this.scroll.onScrolledBottom = this.onChatsScroll;
this.scroll.setVirtualContainer(this.chatList);
//this.scroll.attachSentinels();
@ -588,13 +589,15 @@ export class AppDialogsManager { @@ -588,13 +589,15 @@ export class AppDialogsManager {
}
}); */
new Scrollable(this.folders.menuScrollContainer, 'x');
const foldersScrollable = new ScrollableX(this.folders.menuScrollContainer);
this.chatsContainer.prepend(this.folders.menuScrollContainer);
const selectTab = horizontalMenu(this.folders.menu, this.folders.container, (id, tabContent) => {
/* if(id != 0) {
id += 1;
} */
foldersScrollable.scrollIntoView(this.folders.menu.firstElementChild.children[id] as HTMLElement, true, 250);
id = +tabContent.dataset.filterID || 0;
if(this.filterID == id) return;

2
src/lib/appManagers/appDownloadManager.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { $rootScope } from "../utils";
import apiManager from "../mtproto/mtprotoworker";
import { deferredPromise, CancellablePromise } from "../polyfill";
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
import type { DownloadOptions } from "../mtproto/apiFileManager";
import { getFileNameByLocation } from "../bin_utils";
import { InputFile } from "../../layer";

29
src/lib/appManagers/appImManager.ts

@ -28,7 +28,7 @@ import appStickersManager from './appStickersManager'; @@ -28,7 +28,7 @@ import appStickersManager from './appStickersManager';
import AvatarElement from '../../components/avatar';
import appInlineBotsManager from './AppInlineBotsManager';
import StickyIntersector from '../../components/stickyIntersector';
import { mediaSizes, touchSupport, isAndroid, isApple } from '../config';
import { touchSupport } from '../config';
import animationIntersector from '../../components/animationIntersector';
import PopupStickers from '../../components/popupStickers';
import PopupDatePicker from '../../components/popupDatepicker';
@ -40,6 +40,8 @@ import { InputNotifyPeer, InputPeerNotifySettings } from '../../layer'; @@ -40,6 +40,8 @@ import { InputNotifyPeer, InputPeerNotifySettings } from '../../layer';
import { ChatAudio } from '../../components/chat/audio';
import { ChatContextMenu } from '../../components/chat/contextMenu';
import { ChatSearch } from '../../components/chat/search';
import mediaSizes from '../../helpers/mediaSizes';
import { isAndroid, isApple } from '../../helpers/userAgent';
//console.log('appImManager included33!');
@ -148,7 +150,7 @@ export class AppImManager { @@ -148,7 +150,7 @@ export class AppImManager {
parseMenuButtonsTo(this.menuButtons, this.columnEl.querySelector('.chat-more-button').firstElementChild.children);
this.chatAudio = new ChatAudio();
this.topbar.insertBefore(this.chatAudio.container, this.chatInfo.nextElementSibling);
this.chatInfo.nextElementSibling.prepend(this.chatAudio.container);
apiManager.getUserID().then((id) => {
this.myID = $rootScope.myID = id;
@ -738,7 +740,7 @@ export class AppImManager { @@ -738,7 +740,7 @@ export class AppImManager {
}, {once: true});
newPinned.append(close);
this.topbar.insertBefore(newPinned, this.btnJoin);
this.btnJoin.parentElement.insertBefore(newPinned, this.btnJoin);
this.topbar.classList.add('is-pinned-shown');
if(this.pinnedMessageContainer) {
@ -833,7 +835,7 @@ export class AppImManager { @@ -833,7 +835,7 @@ export class AppImManager {
}
public setScroll() {
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, 300);
this.scrollable = new Scrollable(this.bubblesContainer, 'IM', this.chatInner, 300);
/* const getScrollOffset = () => {
//return Math.round(Math.max(300, appPhotosManager.windowH / 1.5));
@ -847,8 +849,6 @@ export class AppImManager { @@ -847,8 +849,6 @@ export class AppImManager {
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, getScrollOffset()); */
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);
@ -1004,7 +1004,8 @@ export class AppImManager { @@ -1004,7 +1004,8 @@ export class AppImManager {
////console.time('appImManager: pre render start');
if(peerID == 0) {
appSidebarRight.toggleSidebar(false);
this.topbar.style.display = this.chatInput.style.display = this.goDownBtn.style.display = 'none';
this.topbar.style.display = this.chatInput.style.display = 'none';
this.goDownBtn.classList.add('hide');
this.cleanup(true);
this.peerID = $rootScope.selectedPeerID = 0;
$rootScope.$broadcast('peer_changed', this.peerID);
@ -1253,7 +1254,7 @@ export class AppImManager { @@ -1253,7 +1254,7 @@ export class AppImManager {
else title = appPeersManager.getPeerTitle(this.peerID);
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title;
this.goDownBtn.style.display = '';
this.goDownBtn.classList.remove('hide');
this.setPeerStatus(true);
});
@ -1320,10 +1321,10 @@ export class AppImManager { @@ -1320,10 +1321,10 @@ 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.scrollable.scrollTo(this.scrollable.scrollHeight - 1, 'top', false, true);
this.messagesQueuePromise.then(() => {
this.log('messagesQueuePromise after:', this.chatInner.childElementCount, this.scrollable.scrollHeight);
this.scrollable.scrollTo(this.scrollable.scrollHeight, true, true);
this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true);
setTimeout(() => {
this.log('messagesQueuePromise afterafter:', this.chatInner.childElementCount, this.scrollable.scrollHeight);
@ -1974,17 +1975,17 @@ export class AppImManager { @@ -1974,17 +1975,17 @@ export class AppImManager {
quoteTextDiv.append(nameEl);
}
if(webpage.title) {
if(webpage.rTitle) {
let titleDiv = document.createElement('div');
titleDiv.classList.add('title');
titleDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.title);
titleDiv.innerHTML = webpage.rTitle;
quoteTextDiv.append(titleDiv);
}
if(webpage.description) {
if(webpage.rDescription) {
let textDiv = document.createElement('div');
textDiv.classList.add('text');
textDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.description);
textDiv.innerHTML = webpage.rDescription;
quoteTextDiv.append(textDiv);
}

16
src/lib/appManagers/appMediaViewer.ts

@ -9,11 +9,13 @@ import appDocsManager, {MyDocument} from "./appDocsManager"; @@ -9,11 +9,13 @@ import appDocsManager, {MyDocument} from "./appDocsManager";
import VideoPlayer from "../mediaPlayer";
import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc";
import AvatarElement from "../../components/avatar";
import LazyLoadQueue, { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
import { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
import appForward from "../../components/appForward";
import { isSafari, mediaSizes, touchSupport } from "../config";
import { deferredPromise } from "../polyfill";
import { touchSupport } from "../config";
import appMediaPlaybackController from "../../components/appMediaPlaybackController";
import { deferredPromise } from "../../helpers/cancellablePromise";
import mediaSizes from "../../helpers/mediaSizes";
import { isSafari } from "../../helpers/userAgent";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -355,7 +357,7 @@ export class AppMediaViewer { @@ -355,7 +357,7 @@ export class AppMediaViewer {
top = rect.top;
}
transform += `translate(${left}px,${top}px) `;
transform += `translate3d(${left}px,${top}px,0) `;
/* if(wasActive) {
left = fromRight === 1 ? appPhotosManager.windowW / 2 : -(containerRect.width + appPhotosManager.windowW / 2);
@ -585,7 +587,7 @@ export class AppMediaViewer { @@ -585,7 +587,7 @@ export class AppMediaViewer {
// чтобы проверить установленную позицию - раскомментировать
//throw '';
mover.style.transform = `translate(${containerRect.left}px,${containerRect.top}px) scale(1,1)`;
mover.style.transform = `translate3d(${containerRect.left}px,${containerRect.top}px,0) scale(1,1)`;
//mover.style.transform = `translate(-50%,-50%) scale(1,1)`;
if(aspecter) {
@ -691,7 +693,7 @@ export class AppMediaViewer { @@ -691,7 +693,7 @@ export class AppMediaViewer {
if(mover.classList.contains('center')) {
//const rect = mover.getBoundingClientRect();
const rect = this.content.container.getBoundingClientRect();
mover.style.transform = `translate(${rect.left}px,${rect.top}px)`;
mover.style.transform = `translate3d(${rect.left}px,${rect.top}px,0)`;
mover.classList.remove('center');
void mover.offsetLeft; // reflow
mover.classList.remove('no-transition');
@ -712,7 +714,7 @@ export class AppMediaViewer { @@ -712,7 +714,7 @@ export class AppMediaViewer {
const rect = mover.getBoundingClientRect();
const newTransform = mover.style.transform.replace(/translate\((.+?),/, (match, p1) => {
const newTransform = mover.style.transform.replace(/translate3d\((.+?),/, (match, p1) => {
const x = toLeft ? -rect.width : windowW;
//const x = toLeft ? -(rect.right + (rect.width / 2)) : windowW / 2;

6
src/lib/appManagers/appMessagesManager.ts

@ -17,7 +17,7 @@ import serverTimeManager from "../mtproto/serverTimeManager"; @@ -17,7 +17,7 @@ import serverTimeManager from "../mtproto/serverTimeManager";
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import appWebPagesManager from "./appWebPagesManager";
import { CancellablePromise, deferredPromise } from "../polyfill";
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
import appPollsManager from "./appPollsManager";
import searchIndexManager from '../searchIndexManager';
import { Modify } from "../../types";
@ -2471,6 +2471,10 @@ export class AppMessagesManager { @@ -2471,6 +2471,10 @@ export class AppMessagesManager {
let messageWrapped = '';
if(text) {
if(text.length > 40) {
text = text.substr(0, 35) + '...';
}
let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true});
messageWrapped = RichTextProcessor.wrapRichText(text, {

2
src/lib/appManagers/appPhotosManager.ts

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
import { calcImageInBox, isObject } from "../utils";
import { bytesFromHex, getFileNameByLocation } from "../bin_utils";
import appDownloadManager from "./appDownloadManager";
import { CancellablePromise } from "../polyfill";
import { isSafari } from "../../helpers/userAgent";
import { FileLocation, InputFileLocation, Photo, PhotoSize } from "../../layer";
import { MyDocument } from "./appDocsManager";
import { CancellablePromise } from "../../helpers/cancellablePromise";
export type MyPhoto = Photo.photo;

8
src/lib/appManagers/appSidebarLeft.ts

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
//import { logger } from "../polyfill";
import appDialogsManager, { AppArchivedTab, archivedTab } from "./appDialogsManager";
import { $rootScope, findUpTag, findUpClassName } from "../utils";
import { $rootScope, findUpTag, findUpClassName, formatNumber } from "../utils";
import appImManager from "./appImManager";
import AppSearch, { SearchGroup } from "../../components/appSearch";
import { parseMenuButtonsTo } from "../../components/misc";
import appUsersManager from "./appUsersManager";
import Scrollable from "../../components/scrollable_new";
import Scrollable, { ScrollableX } from "../../components/scrollable_new";
import appPeersManager from "../appManagers/appPeersManager";
import AvatarElement from "../../components/avatar";
import AppNewChannelTab from "../../components/sidebarLeft/newChannel";
@ -175,7 +175,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -175,7 +175,7 @@ export class AppSidebarLeft extends SidebarSlider {
peopleContainer.classList.add('search-group-scrollable');
peopleContainer.append(this.searchGroups.people.list);
this.searchGroups.people.container.append(peopleContainer);
let peopleScrollable = new Scrollable(peopleContainer, 'x');
let peopleScrollable = new ScrollableX(peopleContainer);
parseMenuButtonsTo(this.buttons, this.menuEl.children);
parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children);
@ -251,7 +251,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -251,7 +251,7 @@ export class AppSidebarLeft extends SidebarSlider {
});
$rootScope.$on('dialogs_archived_unread', (e) => {
this.archivedCount.innerText = '' + e.detail.count;
this.archivedCount.innerText = '' + formatNumber(e.detail.count, 1);
});
appUsersManager.getTopPeers().then(peers => {

67
src/lib/appManagers/appSidebarRight.ts

@ -16,16 +16,19 @@ import { wrapDocument, wrapAudio } from "../../components/wrappers"; @@ -16,16 +16,19 @@ import { wrapDocument, wrapAudio } from "../../components/wrappers";
import AppSearch, { SearchGroup } from "../../components/appSearch";
import AvatarElement from "../../components/avatar";
import appForward from "../../components/appForward";
import { mediaSizes } from "../config";
import SidebarSlider from "../../components/slider";
import SearchInput from "../../components/searchInput";
import { horizontalMenu } from "../../components/horizontalMenu";
import AppStickersTab from "../../components/sidebarRight/stickers";
import AppPollResultsTab from "../../components/sidebarRight/pollResults";
import AppGifsTab from "../../components/sidebarRight/gifs";
import mediaSizes, { ScreenSize } from "../../helpers/mediaSizes";
import { isSafari } from "../../helpers/userAgent";
const testScroll = false;
const COLUMN_ACTIVE_CLASSNAME = 'is-right-column-shown';
let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => {
if(el.childElementCount > 1) {
@ -183,7 +186,7 @@ export class AppSidebarRight extends SidebarSlider { @@ -183,7 +186,7 @@ export class AppSidebarRight extends SidebarSlider {
let container = this.profileContentEl.querySelector('.content-container .tabs-container') as HTMLDivElement;
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs');
this.scroll = new Scrollable(this.profileContainer, 'y', 'SR', undefined, 400);
this.scroll = new Scrollable(this.profileContainer, 'SR', undefined, 400);
this.scroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && this.sharedMediaSelected.childElementCount/* && false */) {
this.log('onScrolledBottom will load media');
@ -194,6 +197,10 @@ export class AppSidebarRight extends SidebarSlider { @@ -194,6 +197,10 @@ export class AppSidebarRight extends SidebarSlider {
horizontalMenu(this.profileTabs, container, (id, tabContent) => {
if(this.prevTabID == id) return;
if(this.prevTabID != -1) {
this.onTransitionStart();
}
this.sharedMediaType = this.sharedMediaTypes[id];
this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement;
@ -213,6 +220,7 @@ export class AppSidebarRight extends SidebarSlider { @@ -213,6 +220,7 @@ export class AppSidebarRight extends SidebarSlider {
this.prevTabID = id;
}, () => {
this.scroll.onScroll();
this.onTransitionEnd();
});
let sidebarCloseBtn = this.sidebarEl.querySelector('.sidebar-close-button') as HTMLButtonElement;
@ -252,8 +260,31 @@ export class AppSidebarRight extends SidebarSlider { @@ -252,8 +260,31 @@ export class AppSidebarRight extends SidebarSlider {
//let checked = this.profileElements.notificationsCheckbox.checked;
appImManager.mutePeer(this.peerID);
});
mediaSizes.addListener('changeScreen', (from, to) => {
if(from !== undefined && to == ScreenSize.medium) {
this.toggleSidebar(false);
}
});
}
private onTransitionStart = () => {
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
const container = this.scroll.container;
if(container.style.overflowY !== 'hidden') {
const scrollBarWidth = container.offsetWidth - container.clientWidth;
container.style.overflowY = 'hidden';
container.style.paddingRight = `${scrollBarWidth}px`;
}
};
private onTransitionEnd = () => {
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
const container = this.scroll.container;
container.style.overflowY = '';
container.style.paddingRight = '0';
};
public beginSearch() {
this.toggleSidebar(true);
this.searchContainer.classList.add('active');
@ -263,7 +294,7 @@ export class AppSidebarRight extends SidebarSlider { @@ -263,7 +294,7 @@ export class AppSidebarRight extends SidebarSlider {
public toggleSidebar(enable?: boolean) {
/////this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl));
const active = this.sidebarEl.classList.contains('active');
const active = document.body.classList.contains(COLUMN_ACTIVE_CLASSNAME);
let willChange: boolean;
if(enable !== undefined) {
if(enable) {
@ -286,9 +317,15 @@ export class AppSidebarRight extends SidebarSlider { @@ -286,9 +317,15 @@ export class AppSidebarRight extends SidebarSlider {
}
const set = () => {
this.sidebarEl.classList.toggle('active', enable);
document.body.classList.toggle(COLUMN_ACTIVE_CLASSNAME, enable);
};
set();
return new Promise(resolve => {
setTimeout(resolve, 200);
});
//return Promise.resolve();
return new Promise((resolve, reject) => {
const hidden: {element: HTMLDivElement, height: number}[] = [];
const observer = new IntersectionObserver((entries) => {
@ -483,6 +520,12 @@ export class AppSidebarRight extends SidebarSlider { @@ -483,6 +520,12 @@ export class AppSidebarRight extends SidebarSlider {
const needBlurCallback = needBlur ? () => {
//void img.offsetLeft; // reflow
img.style.opacity = '';
if(thumb) {
window.setTimeout(() => {
thumb.remove();
}, 200);
}
} : undefined;
renderImageFromUrl(img, url, needBlurCallback);
}
@ -515,7 +558,7 @@ export class AppSidebarRight extends SidebarSlider { @@ -515,7 +558,7 @@ export class AppSidebarRight extends SidebarSlider {
});
const timeout = setTimeout(() => {
this.log('did not loaded', thumb, media, isDownloaded, sizes);
this.log('didn\'t load', thumb, media, isDownloaded, sizes);
reject();
}, 1e3);
});
@ -780,8 +823,10 @@ export class AppSidebarRight extends SidebarSlider { @@ -780,8 +823,10 @@ export class AppSidebarRight extends SidebarSlider {
const inputFilter = contentToSharedMap[key];
if(!this.historiesStorage[this.peerID] || !this.historiesStorage[this.peerID][inputFilter]) {
const parent = this.sharedMedia[key].parentElement;
if(!parent.querySelector('.preloader')) {
putPreloader(parent, true);
if(!testScroll) {
if(!parent.querySelector('.preloader')) {
putPreloader(parent, true);
}
}
const empty = parent.querySelector('.content-empty');
@ -792,16 +837,14 @@ export class AppSidebarRight extends SidebarSlider { @@ -792,16 +837,14 @@ export class AppSidebarRight extends SidebarSlider {
});
if(testScroll) {
for(let i = 0; i < 30; ++i) {
//div.insertAdjacentHTML('beforeend', `<div style="background-image: url(assets/img/camomile.jpg);"></div>`);
for(let i = 0; i < 1500; ++i) {
let div = document.createElement('div');
div.insertAdjacentHTML('beforeend', `<img class="media-image" src="assets/img/camomile.jpg">`);
div.classList.add('media-item');
div.dataset.id = '' + (i / 3 | 0);
div.innerText = '' + (i / 3 | 0);
//div.innerText = '' + (i / 3 | 0);
this.sharedMedia.contentMedia.append(div);
}
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
}
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media

2
src/lib/bin_utils.ts

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
// @ts-ignore
import {BigInteger, SecureRandom} from 'jsbn';
import { InputFileLocation, FileLocation } from '../layer';
import type { InputFileLocation, FileLocation } from '../layer';
/// #if !MTPROTO_WORKER
// @ts-ignore

73
src/lib/config.ts

File diff suppressed because one or more lines are too long

2
src/lib/crypto/crypto_methods.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { convertToArrayBuffer, convertToByteArray } from "../bin_utils";
import { InputCheckPasswordSRP } from "../../layer";
import type { InputCheckPasswordSRP } from "../../layer";
export default abstract class CryptoWorkerMethods {
abstract performTaskWorker<T>(task: string, ...args: any[]): Promise<T>;

13
src/lib/crypto/cryptoworker.ts

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
import {dT} from '../bin_utils';
import CryptoWorkerMethods from './crypto_methods';
type Task = {
@ -24,7 +23,7 @@ class CryptoWorker extends CryptoWorkerMethods { @@ -24,7 +23,7 @@ class CryptoWorker extends CryptoWorkerMethods {
constructor() {
super();
console.log(dT(), 'CW constructor');
console.log('CW constructor');
/// #if MTPROTO_WORKER
Promise.all([
@ -60,7 +59,7 @@ class CryptoWorker extends CryptoWorkerMethods { @@ -60,7 +59,7 @@ class CryptoWorker extends CryptoWorkerMethods {
tmpWorker.onmessage = (e: any) => {
if(!this.webWorker) {
this.webWorker = tmpWorker;
console.info(dT(), 'CW set webWorker');
console.info('CW set webWorker');
this.releasePending();
} else {
this.finalizeTask(e.data.taskID, e.data.result);
@ -76,17 +75,19 @@ class CryptoWorker extends CryptoWorkerMethods { @@ -76,17 +75,19 @@ class CryptoWorker extends CryptoWorkerMethods {
/// #endif
}
/// #if !MTPROTO_WORKER
private finalizeTask(taskID: number, result: any) {
let deferred = this.awaiting[taskID];
if(deferred !== undefined) {
this.debug && console.log(dT(), 'CW done', deferred.taskName, result);
this.debug && console.log('CW done', deferred.taskName, result);
deferred.resolve(result);
delete this.awaiting[taskID];
}
}
/// #endif
public performTaskWorker<T>(task: string, ...args: any[]) {
this.debug && console.log(dT(), 'CW start', task, args);
this.debug && console.log('CW start', task, args);
/// #if MTPROTO_WORKER
return Promise.resolve<T>(this.utils[task](...args));
@ -109,6 +110,7 @@ class CryptoWorker extends CryptoWorkerMethods { @@ -109,6 +110,7 @@ class CryptoWorker extends CryptoWorkerMethods {
/// #endif
}
/// #if !MTPROTO_WORKER
private releasePending() {
if(this.webWorker) {
this.pending.forEach(pending => {
@ -118,6 +120,7 @@ class CryptoWorker extends CryptoWorkerMethods { @@ -118,6 +120,7 @@ class CryptoWorker extends CryptoWorkerMethods {
this.pending.length = 0;
}
}
/// #endif
}
const cryptoWorker = new CryptoWorker();

368
src/lib/idb.ts

@ -1,368 +0,0 @@ @@ -1,368 +0,0 @@
import {blobConstruct, bytesToBase64, blobSafeMimeType, dataUrlToBlob} from './bin_utils';
import FileManager from './filemanager';
import { logger } from './logger';
class IdbFileStorage {
public dbName = 'cachedFiles';
public dbStoreName = 'files';
public dbVersion = 2;
public openDbPromise: Promise<IDBDatabase>;
public storageIsAvailable = true;
public name = 'IndexedDB';
private log: ReturnType<typeof logger> = logger('IDB');
constructor() {
this.openDatabase(true);
}
public isAvailable() {
return this.storageIsAvailable;
}
public openDatabase(createNew = false): Promise<IDBDatabase> {
if(this.openDbPromise && !createNew) {
return this.openDbPromise;
}
const createObjectStore = (db: IDBDatabase) => {
db.createObjectStore(this.dbStoreName);
};
try {
var request = indexedDB.open(this.dbName, this.dbVersion);
if(!request) {
throw new Error();
}
} catch(error) {
this.log.error('error opening db', error.message)
this.storageIsAvailable = false;
return Promise.reject(error);
}
let finished = false;
setTimeout(() => {
if(!finished) {
request.onerror({type: 'IDB_CREATE_TIMEOUT'} as Event);
}
}, 3000);
return this.openDbPromise = new Promise<IDBDatabase>((resolve, reject) => {
request.onsuccess = (event) => {
finished = true;
const db = request.result;
let calledNew = false;
this.log('Opened');
db.onerror = (error) => {
this.storageIsAvailable = false;
this.log.error('Error creating/accessing IndexedDB database', error);
reject(error);
};
db.onclose = (e) => {
this.log.error('closed:', e);
!calledNew && this.openDatabase();
};
db.onabort = (e) => {
this.log.error('abort:', e);
const transaction = e.target as IDBTransaction;
this.openDatabase(calledNew = true);
if(transaction.onerror) {
transaction.onerror(e);
}
db.close();
};
db.onversionchange = (e) => {
this.log.error('onversionchange, lol?');
};
resolve(db);
};
request.onerror = (event) => {
finished = true;
this.storageIsAvailable = false;
this.log.error('Error creating/accessing IndexedDB database', event);
reject(event);
};
request.onupgradeneeded = (event) => {
finished = true;
this.log.warn('performing idb upgrade from', event.oldVersion, 'to', event.newVersion);
// @ts-ignore
var db = event.target.result as IDBDatabase;
if(event.oldVersion == 1) {
db.deleteObjectStore(this.dbStoreName);
}
createObjectStore(db);
};
});
}
public deleteFile(fileName: string): Promise<void> {
//return Promise.resolve();
return this.openDatabase().then((db) => {
try {
this.log('Delete file: `' + fileName + '`');
var objectStore = db.transaction([this.dbStoreName], 'readwrite')
.objectStore(this.dbStoreName);
var request = objectStore.delete(fileName);
} catch(error) {
return Promise.reject(error);
}
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
this.log.error('deleteFile request not finished!', fileName, request);
resolve();
}, 3000);
request.onsuccess = (event) => {
this.log('deleted file', event);
resolve();
clearTimeout(timeout);
};
request.onerror = (error) => {
reject(error);
clearTimeout(timeout);
};
});
});
}
public saveFile(fileName: string, blob: Blob | Uint8Array): Promise<Blob> {
return Promise.resolve(blobConstruct([blob]));
return this.openDatabase().then((db) => {
if(!(blob instanceof Blob)) {
blob = blobConstruct([blob]) as Blob;
}
this.log('saveFile:', fileName, blob);
const handleError = (error: Error) => {
this.log.error('saveFile transaction error:', fileName, blob, db, error, error && error.name);
if((!error || error.name === 'InvalidStateError')/* && false */) {
setTimeout(() => {
this.saveFile(fileName, blob);
}, 2e3);
} else {
//console.error('IndexedDB saveFile transaction error:', error, error && error.name);
}
};
try {
const transaction = db.transaction([this.dbStoreName], 'readwrite');
transaction.onerror = (e) => {
handleError(transaction.error);
};
transaction.oncomplete = (e) => {
this.log('saveFile transaction complete:', fileName);
};
/* transaction.addEventListener('abort', (e) => {
//handleError();
this.log.error('IndexedDB: saveFile transaction abort!', transaction.error);
}); */
const objectStore = transaction.objectStore(this.dbStoreName);
var request = objectStore.put(blob, fileName);
} catch(error) {
handleError(error);
return blob;
/* this.storageIsAvailable = false;
throw error; */
}
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
this.log.error('saveFile request not finished', fileName, request);
}, 3000);
request.onsuccess = (event) => {
resolve(blob as Blob);
clearTimeout(timeout);
};
request.onerror = (error) => {
reject(error);
clearTimeout(timeout);
};
});
});
}
public saveFileBase64(db: IDBDatabase, fileName: string, blob: Blob | any): Promise<Blob> {
if(this.getBlobSize(blob) > 10 * 1024 * 1024) {
return Promise.reject();
}
if(!(blob instanceof Blob)) {
var safeMimeType = blobSafeMimeType(blob.type || 'image/jpeg');
var address = 'data:' + safeMimeType + ';base64,' + bytesToBase64(blob);
return this.storagePutB64String(db, fileName, address).then(() => {
return blob;
});
}
try {
var reader = new FileReader();
} catch (e) {
this.storageIsAvailable = false;
return Promise.reject();
}
let promise = new Promise<Blob>((resolve, reject) => {
reader.onloadend = () => {
this.storagePutB64String(db, fileName, reader.result as string).then(() => {
resolve(blob);
}, reject);
}
reader.onerror = reject;
});
try {
reader.readAsDataURL(blob);
} catch (e) {
this.storageIsAvailable = false;
return Promise.reject();
}
return promise;
}
public storagePutB64String(db: IDBDatabase, fileName: string, b64string: string) {
try {
var objectStore = db.transaction([this.dbStoreName], 'readwrite')
.objectStore(this.dbStoreName);
var request = objectStore.put(b64string, fileName);
} catch(error) {
this.storageIsAvailable = false;
return Promise.reject(error);
}
return new Promise((resolve, reject) => {
request.onsuccess = function(event) {
resolve();
};
request.onerror = reject;
});
}
public getBlobSize(blob: any) {
return blob.size || blob.byteLength || blob.length;
}
public getFile(fileName: string): Promise<Blob> {
//return Promise.reject();
return this.openDatabase().then((db) => {
this.log('getFile pre:', fileName);
try {
const transaction = db.transaction([this.dbStoreName], 'readonly');
transaction.onabort = (e) => {
this.log.error('getFile transaction onabort?', e);
};
const objectStore = transaction.objectStore(this.dbStoreName);
var request = objectStore.get(fileName);
//this.log.log('IDB getFile:', fileName, request);
} catch(err) {
this.log.error('getFile error:', err, fileName, request, request.error);
}
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
this.log.error('getFile request not finished!', fileName, request);
reject();
}, 3000);
request.onsuccess = function(event) {
const result = request.result;
if(result === undefined) {
reject();
} else if(typeof result === 'string' &&
result.substr(0, 5) === 'data:') {
resolve(dataUrlToBlob(result));
} else {
resolve(result);
}
clearTimeout(timeout);
}
request.onerror = () => {
clearTimeout(timeout);
reject();
};
});
});
}
public getAllKeys(): Promise<Array<string>> {
console.time('getAllEntries');
return this.openDatabase().then((db) => {
var objectStore = db.transaction([this.dbStoreName], 'readonly')
.objectStore(this.dbStoreName);
var request = objectStore.getAllKeys();
return new Promise((resolve, reject) => {
request.onsuccess = function(event) {
// @ts-ignore
var result = event.target.result;
resolve(result);
console.timeEnd('getAllEntries');
}
request.onerror = reject;
});
});
}
public isFileExists(fileName: string): Promise<boolean> {
console.time('isFileExists');
return this.openDatabase().then((db) => {
var objectStore = db.transaction([this.dbStoreName], 'readonly')
.objectStore(this.dbStoreName);
var request = objectStore.openCursor(fileName);
return new Promise((resolve, reject) => {
request.onsuccess = function(event) {
// @ts-ignore
var cursor = event.target.result;
resolve(!!cursor);
console.timeEnd('isFileExists');
}
request.onerror = reject;
});
});
}
public getFileWriter(fileName: string, mimeType: string) {
var fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob) => {
return this.saveFile(fileName, blob);
});
return Promise.resolve(fakeWriter);
}
}
const idbFileStorage = new IdbFileStorage();
(window as any).IdbFileStorage = idbFileStorage;
export default idbFileStorage;

5
src/lib/lottie.ts

@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
// @ts-ignore
//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;

66
src/lib/lottieLoader.ts

@ -1,8 +1,10 @@ @@ -1,8 +1,10 @@
import { isApple, mediaSizes, isSafari } from "./config";
import { logger, LogLevels } from "./logger";
import animationIntersector from "../components/animationIntersector";
import apiManager from "./mtproto/mtprotoworker";
import { copy } from "./utils";
import EventListenerBase from "../helpers/eventListenerBase";
import mediaSizes from "../helpers/mediaSizes";
import { isApple, isSafari } from "../helpers/userAgent";
let convert = (value: number) => {
return Math.round(Math.min(Math.max(value, 0), 1) * 255);
@ -21,7 +23,12 @@ type RLottieOptions = { @@ -21,7 +23,12 @@ type RLottieOptions = {
needUpscale?: true
};
export class RLottiePlayer {
export class RLottiePlayer extends EventListenerBase<{
enterFrame: (frameNo: number) => void,
ready: () => void,
firstFrame: () => void,
cached: () => void
}> {
public static reqId = 0;
public reqId = 0;
@ -34,13 +41,6 @@ export class RLottiePlayer { @@ -34,13 +41,6 @@ export class RLottiePlayer {
public width = 0;
public height = 0;
public listeners: Partial<{
[k in RLottiePlayerListeners]: Array<{callback: (res: any) => void, once?: true}>
}> = {};
public listenerResults: Partial<{
[k in RLottiePlayerListeners]: any
}> = {};
public el: HTMLElement;
public canvas: HTMLCanvasElement;
public context: CanvasRenderingContext2D;
@ -75,6 +75,8 @@ export class RLottiePlayer { @@ -75,6 +75,8 @@ export class RLottiePlayer {
worker: QueryableWorker,
options: RLottieOptions
}) {
super(true);
this.reqId = ++RLottiePlayer['reqId'];
this.el = el;
this.worker = worker;
@ -138,37 +140,6 @@ export class RLottiePlayer { @@ -138,37 +140,6 @@ export class RLottiePlayer {
this.frames = {};
}
public addListener(name: RLottiePlayerListeners, callback: (res?: any) => void, once?: true) {
(this.listeners[name] ?? (this.listeners[name] = [])).push({callback, once});
if(this.listenerResults.hasOwnProperty(name)) {
callback(this.listenerResults[name]);
if(once) {
this.removeListener(name, callback);
}
}
}
public removeListener(name: RLottiePlayerListeners, callback: (res?: any) => void) {
if(this.listeners[name]) {
this.listeners[name].findAndSplice(l => l.callback == callback);
}
}
public setListenerResult(name: RLottiePlayerListeners, value?: any) {
this.listenerResults[name] = value;
if(this.listeners[name]) {
this.listeners[name].forEach(listener => {
listener.callback(value);
if(listener.once) {
this.removeListener(name, listener.callback);
}
});
}
}
public sendQuery(methodName: string, ...args: any[]) {
//console.trace('RLottie sendQuery:', methodName);
this.worker.sendQuery(methodName, this.reqId, ...args);
@ -424,11 +395,12 @@ export class RLottiePlayer { @@ -424,11 +395,12 @@ export class RLottiePlayer {
}
}
class QueryableWorker {
class QueryableWorker extends EventListenerBase<any> {
private worker: Worker;
private listeners: {[name: string]: (...args: any[]) => void} = {};
constructor(url: string, private defaultListener: (data: any) => void = () => {}, onError?: (error: any) => void) {
super();
this.worker = new Worker(url);
if(onError) {
this.worker.onerror = onError;
@ -444,7 +416,7 @@ class QueryableWorker { @@ -444,7 +416,7 @@ class QueryableWorker {
return;
} */
this.listeners[event.data.queryMethodListener](...event.data.queryMethodArguments);
this.setListenerResult(event.data.queryMethodListener, ...event.data.queryMethodArguments);
} else {
this.defaultListener.call(this, event.data);
}
@ -459,14 +431,6 @@ class QueryableWorker { @@ -459,14 +431,6 @@ class QueryableWorker {
this.worker.terminate();
}
public addListener(name: string, listener: (...args: any[]) => void) {
this.listeners[name] = listener;
}
public removeListener(name: string) {
delete this.listeners[name];
}
public sendQuery(queryMethod: string, ...args: any[]) {
var args = Array.prototype.slice.call(arguments, 1);
if(isSafari) {

2
src/lib/mtproto/apiFileManager.ts

@ -3,12 +3,12 @@ import { nextRandomInt, getFileNameByLocation } from "../bin_utils"; @@ -3,12 +3,12 @@ import { nextRandomInt, getFileNameByLocation } from "../bin_utils";
import cacheStorage from "../cacheStorage";
import FileManager from "../filemanager";
import apiManager from "./apiManager";
import { deferredPromise, CancellablePromise } from "../polyfill";
import { logger, LogLevels } from "../logger";
import { isSafari } from "../../helpers/userAgent";
import cryptoWorker from "../crypto/cryptoworker";
import { notifySomeone, notifyAll } from "../../helpers/context";
import { InputFileLocation, FileLocation, InputFile, UploadFile } from "../../layer";
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
type Delayed = {
offset: number,

4
src/lib/mtproto/mtproto.service.ts

@ -2,9 +2,9 @@ import { isSafari } from '../../helpers/userAgent'; @@ -2,9 +2,9 @@ import { isSafari } from '../../helpers/userAgent';
import { logger, LogLevels } from '../logger';
import type { DownloadOptions } from './apiFileManager';
import type { WorkerTaskTemplate } from '../../types';
import { deferredPromise, CancellablePromise } from '../polyfill';
import { notifySomeone } from '../../helpers/context';
import { InputFileLocation, FileLocation, UploadFile } from '../../layer';
import type { InputFileLocation, FileLocation, UploadFile } from '../../layer';
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
const log = logger('SW', LogLevels.error/* | LogLevels.debug | LogLevels.log */);
const ctx = self as any as ServiceWorkerGlobalScope;

5
src/lib/mtproto/mtproto.worker.ts

@ -1,15 +1,14 @@ @@ -1,15 +1,14 @@
// just to include
import {secureRandom, CancellablePromise} from '../polyfill';
import {secureRandom} from '../polyfill';
secureRandom;
import apiManager from "./apiManager";
import AppStorage from '../storage';
import cryptoWorker from "../crypto/cryptoworker";
import networkerFactory from "./networkerFactory";
import apiFileManager, { ApiFileManager } from './apiFileManager';
import apiFileManager from './apiFileManager';
import { logger, LogLevels } from '../logger';
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
import { UploadFile } from '../../layer';
const log = logger('DW', LogLevels.error);

4
src/lib/opusDecodeController.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { isSafari } from "./config";
import { isSafari } from "../helpers/userAgent";
import { logger, LogLevels } from "./logger";
type Result = {
@ -132,7 +132,7 @@ export class OpusDecodeController { @@ -132,7 +132,7 @@ export class OpusDecodeController {
}, isSafari ? undefined : [task.pages.buffer]);
//}, 1e3);
task.timeout = setTimeout(() => {
task.timeout = window.setTimeout(() => {
this.log.error('decode timeout'/* , task */);
this.terminateWorkers(true);

64
src/lib/polyfill.ts

@ -4,70 +4,6 @@ import {SecureRandom} from 'jsbn'; @@ -4,70 +4,6 @@ import {SecureRandom} from 'jsbn';
export const secureRandom = new SecureRandom();
export interface CancellablePromise<T> extends Promise<T> {
resolve?: (...args: any[]) => void,
reject?: (...args: any[]) => void,
cancel?: () => void,
notify?: (...args: any[]) => void,
notifyAll?: (...args: any[]) => void,
lastNotify?: any,
listeners?: Array<(...args: any[]) => void>,
addNotifyListener?: (callback: (...args: any[]) => void) => void,
isFulfilled?: boolean,
isRejected?: boolean
}
export function deferredPromise<T>() {
let deferredHelper: any = {
isFulfilled: false,
isRejected: false,
notify: () => {},
notifyAll: (...args: any[]) => {
deferredHelper.lastNotify = args;
deferredHelper.listeners.forEach((callback: any) => callback(...args));
},
lastNotify: undefined,
listeners: [],
addNotifyListener: (callback: (...args: any[]) => void) => {
if(deferredHelper.lastNotify) {
callback(...deferredHelper.lastNotify);
}
deferredHelper.listeners.push(callback);
}
};
let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => {
deferredHelper.resolve = (value: T) => {
if(deferred.isFulfilled) return;
deferred.isFulfilled = true;
resolve(value);
};
deferredHelper.reject = (...args: any[]) => {
if(deferred.isRejected) return;
deferred.isRejected = true;
reject(...args);
};
});
deferred.finally(() => {
deferred.notify = null;
deferred.listeners.length = 0;
deferred.lastNotify = null;
});
Object.assign(deferred, deferredHelper);
return deferred;
}
Object.defineProperty(Uint8Array.prototype, 'hex', {
get: function(): string {
return bytesToHex([...this]);

2
src/lib/webp/webpWorkerController.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import WebpWorker from 'worker-loader!./webp.worker';
import { CancellablePromise, deferredPromise } from '../polyfill';
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
import apiManagerProxy from '../mtproto/mtprotoworker';
export type WebpConvertTask = {

2
src/pages/pageAuthCode.ts

@ -8,7 +8,7 @@ import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader'; @@ -8,7 +8,7 @@ import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader';
import apiManager from '../lib/mtproto/mtprotoworker';
import Page from './page';
import { App } from '../lib/mtproto/mtproto_config';
import { mediaSizes } from '../lib/config';
import mediaSizes from '../helpers/mediaSizes';
let authCode: {
_: string, // 'auth.sentCode'

2
src/pages/pagePassword.ts

@ -7,10 +7,10 @@ import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader'; @@ -7,10 +7,10 @@ import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader';
//import passwordManager from '../lib/mtproto/passwordManager';
import apiManager from '../lib/mtproto/mtprotoworker';
import Page from './page';
import { mediaSizes } from '../lib/config';
import passwordManager from '../lib/mtproto/passwordManager';
import { cancelEvent } from '../lib/utils';
import { AccountPassword } from '../layer';
import mediaSizes from '../helpers/mediaSizes';
let onFirstMount = (): Promise<any> => {
let needFrame = 0;

2
src/scss/components/_global.scss

@ -105,7 +105,7 @@ Utility Classes @@ -105,7 +105,7 @@ Utility Classes
}
.position-center {
position: absolute;
position: absolute !important;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);

82
src/scss/partials/_audio.scss

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
.audio {
position: relative;
padding-left: 67px;
min-height: 58px;
max-width: 244px;
overflow: visible!important;
@include respond-to(handhelds) {
padding-left: 45px;
}
&-toggle, &-download {
border-radius: 50%;
background-color: $color-blue;
font-size: 2.3rem;
align-items: center;
@include respond-to(handhelds) {
font-size: 24px !important;
}
}
&-download {
z-index: 2;
}
&-waveform {
height: 23px;
//overflow: visible!important;
rect {
//overflow: visible!important;
fill: #CBCBCB;
&.active {
fill: $color-blue;
}
}
}
&-title {
font-size: 1rem;
color: #000;
user-select: none;
}
&-time, &-subtitle {
font-size: 14px;
color: $color-gray;
margin-top: 3px;
margin-left: -1px;
user-select: none;
@include respond-to(handhelds) {
margin-top: 1px;
font-size: 12px;
}
}
&-title, &:not(.audio-show-progress) &-subtitle {
white-space: nowrap;
overflow: hidden;
max-width: 100%;
text-overflow: ellipsis;
}
@include respond-to(handhelds) {
&-download {
/* background: transparent; */
margin-left: 2px;
margin-top: 1px;
}
&.is-voice {
.audio-download {
margin: 0;
}
}
}
}

59
src/scss/partials/_avatar.scss

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
avatar-element {
color: #fff;
width: 54px;
height: 54px;
line-height: 54px;
border-radius: 50%;
background-color: $color-blue;
text-align: center;
font-size: 1.25em;
/* overflow: hidden; */
position: relative;
user-select: none;
@include respond-to(handhelds) {
font-size: 14px;
}
/* kostil */
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
border-radius: inherit;
user-select: none;
&.fade-in {
animation: fadeIn .2s ease forwards;
}
}
&[class*=" tgico-"] {
line-height: 52px;
font-size: 28px;
}
path {
fill: white;
}
&.is-online:after {
position: absolute;
content: " ";
display: block;
border-radius: 50%;
border: 2px solid white;
background-color: #0ac630;
left: 74%;
top: 73%;
width: 14px;
height: 14px;
}
&.tgico-avatar_deletedaccount {
font-size: 3rem;
}
}

258
src/scss/partials/_chat.scss

@ -1,8 +1,3 @@ @@ -1,8 +1,3 @@
$chat-max-width: 696px;
$time-background: rgba(0, 0, 0, .35);
$handhelds-margin: 5.5625rem;
$beside-button-margin: 2.875rem;
#bubble-contextmenu > div {
padding: 0 84px 0 16px;
@ -50,6 +45,19 @@ $beside-button-margin: 2.875rem; @@ -50,6 +45,19 @@ $beside-button-margin: 2.875rem;
}
}
/* @include respond-to(wide-screens) {
transition: .2s ease-in-out;
align-self: start;
body.is-right-column-shown & {
width: calc(100% - (#{$large-screen} / 4));
}
body.animation-level-0 & {
transition: none;
}
} */
@include respond-to(handhelds) {
.chat-mute-button, .chat-search-button {
display: none;
@ -79,6 +87,48 @@ $beside-button-margin: 2.875rem; @@ -79,6 +87,48 @@ $beside-button-margin: 2.875rem;
.chat-info {
flex: 1 1 auto;
overflow: hidden;
@include respond-to(medium-screens) {
body.is-right-column-shown & {
max-width: calc(100% - var(--right-column-width) * 1.6);
}
}
}
@include respond-to(medium-screens) {
&.is-pinned-shown {
body.is-right-column-shown & {
.chat-info {
max-width: calc(100% - var(--right-column-width) * 1.75);
}
}
}
&.is-pinned-shown.is-audio-shown {
body.is-right-column-shown & {
.chat-info {
max-width: calc(100% - var(--right-column-width) * 2.25);
}
}
}
}
.chat-utils {
display: flex;
align-items: center;
flex: 0 0 auto;
@include respond-to(medium-screens) {
transition: transform var(--layer-transition);
body.is-right-column-shown & {
transform: translate3d(calc(var(--right-column-width) * -1), 0, 0);
}
body.animation-level-0 & {
transition: none;
}
}
}
.chat-join {
@ -153,19 +203,37 @@ $beside-button-margin: 2.875rem; @@ -153,19 +203,37 @@ $beside-button-margin: 2.875rem;
#chat-input {
display: flex;
width: 100%;
max-width: $chat-max-width;
padding-top: .25rem;
max-width: var(--messages-container-width);
padding: .25rem 1rem 0;
flex-direction: column;
flex: 0 0 auto; /* Forces side columns to stay same width */
position: relative;
@include respond-to(medium-screens) {
width: calc(100% - var(--right-column-width));
transition: transform var(--layer-transition);
body.is-right-column-shown & {
transform: translate3d(calc(var(--right-column-width) / -2), 0, 0);
}
body.animation-level-0 & {
transition: none;
}
}
@include respond-to(handhelds) {
padding-left: .5rem;
padding-right: .5rem;
}
.chat-input-container {
display: flex;
align-items: center;
justify-content: space-between;
@include respond-to(handhelds) {
padding: 0 .5rem .5rem;
padding-bottom: .5rem;
}
@include respond-to(not-handhelds) {
@ -346,6 +414,7 @@ $beside-button-margin: 2.875rem; @@ -346,6 +414,7 @@ $beside-button-margin: 2.875rem;
margin-top: -2px;
}
// !WARNING, уже нельзя переименовать в ID, так как это заденет другие селекторы и вёрстка в чате поедет, например - стикер вместе с реплаем
.chat-container {
display: flex;
// padding: 200px;
@ -354,45 +423,46 @@ $beside-button-margin: 2.875rem; @@ -354,45 +423,46 @@ $beside-button-margin: 2.875rem;
//overflow: hidden;
flex-direction: column;
position: relative;
background-image: url('assets/img/camomile_blurred.jpg');
//background: url('assets/img/camomile_blurred.jpg') no-repeat center center;
background-size: cover;
background-position: center center;
//background-attachment: inherit;
flex: 3;
/* @include respond-to(handhelds) {
//background: url("assets/img/camomile_blurred.jpg") no-repeat center center fixed;
background-attachment: fixed;
} */
/* .chat-background, #chat-closed {
position: absolute;
/* left: 0;
top: 0;
width: 100%;
height: 100%; *
left: -1%;
top: -1%;
width: 102%;
height: 102%;
background-image: url('../../../public/assets/img/camomile.jpg');
background-size: cover;
background-position: center center;
display: none;
} */
.chat-background {
overflow: hidden;
&, &:before {
position: absolute !important;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
&:before {
content: "";
display: block;
background-image: url('assets/img/camomile_blurred.jpg');
background-size: cover;
background-position: center center;
@include respond-to(medium-screens) {
body.animation-level-2 & {
// !WARNING, МАГИЧЕСКОЕ ЧИСЛО
margin: -16rem -5rem -20rem 0;
transform: scale(1);
transform-origin: left center;
transition: transform var(--layer-transition);
}
body.animation-level-2.is-right-column-shown & {
transform: scale(.67);
}
}
}
}
#chat-background-canvas {
display: none;
}
/* #chat-closed {
z-index: 3;
}
> *:not(.chat-background) {
z-index: 2;
} */
.input-message {
display: flex;
align-items: center;
@ -402,7 +472,7 @@ $beside-button-margin: 2.875rem; @@ -402,7 +472,7 @@ $beside-button-margin: 2.875rem;
background-color: #fff;
border-radius: 12px;
border-bottom-right-radius: 0;
box-shadow: 0 1px 2px 0 rgba(16, 35, 47, 0.07);
box-shadow: 0 1px 2px 0 rgba(16, 35, 47, .07);
margin-right: 9px;
padding: 4.5px .5rem;
/* padding: 3px .5rem 6px .5rem; */
@ -416,19 +486,19 @@ $beside-button-margin: 2.875rem; @@ -416,19 +486,19 @@ $beside-button-margin: 2.875rem;
@include respond-to(handhelds) {
min-height: 46px;
padding: 0.5px .5rem;
padding: .5px .5rem;
}
&:after {
position: absolute;
bottom: -1px;
width: 11px;
height: 20px;
background-repeat: no-repeat repeat;
content: '';
background-size: 11px 20px;
right: -9px;
background-image: url('assets/img/msg-tail-left.svg');
position: absolute;
bottom: -.1875rem;
right: -8.4px;
width: .5625rem;
height: 1.25rem;
background-repeat: no-repeat no-repeat;
background-position: 0 100%;
background-image: url('assets/img/msg-tail-left-blur.svg');
transform: scaleX(-1);
}
@ -819,12 +889,12 @@ $beside-button-margin: 2.875rem; @@ -819,12 +889,12 @@ $beside-button-margin: 2.875rem;
flex: 1 1 auto; /* Lets middle column shrink/grow to available width */
overflow: hidden;
position: relative;
padding: 0 .5rem;
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow
> .scrollable {
padding: 0 .75rem;
html.is-safari & {
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow
}
> .scrollable {
height: auto;
/* position: absolute;
bottom: 0;
@ -835,20 +905,48 @@ $beside-button-margin: 2.875rem; @@ -835,20 +905,48 @@ $beside-button-margin: 2.875rem;
//flex-direction: unset;
display: block;
@include respond-to(handhelds) {
padding: 0 .5rem;
}
/* display: flex;
flex-direction: column;
justify-content: flex-end; */
// * scrollbar takes some width, don't need to set padding for iOS
html.is-safari:not(.is-ios) & {
padding-left: 6px;
}
@include respond-to(medium-screens) {
transition: transform var(--layer-transition);
body.is-right-column-shown & {
transform: translate3d(calc(var(--right-column-width) / -2), 0, 0);
}
body.animation-level-0 & {
transition: none;
}
}
}
> .preloader-container {
@include respond-to(medium-screens) {
transition: transform var(--layer-transition);
body.is-right-column-shown & {
transform: translate3d(calc(var(--right-column-width) / -2), 0, 0);
}
body.animation-level-0 & {
transition: none;
}
}
}
&:not(.scrolled-down):not(.search-results-active) {
-webkit-mask-image: -webkit-linear-gradient(bottom, transparent, #000 20px);
mask-image: linear-gradient(0deg, transparent 0, #000 20px);
#bubbles-go-down {
& + #bubbles-go-down {
--translateY: 0;
opacity: 1;
}
}
@ -863,7 +961,7 @@ $beside-button-margin: 2.875rem; @@ -863,7 +961,7 @@ $beside-button-margin: 2.875rem;
}
&-container .preloader-circular {
background-color: $time-background;
background-color: var(--message-time-background);
}
}
@ -873,17 +971,29 @@ $beside-button-margin: 2.875rem; @@ -873,17 +971,29 @@ $beside-button-margin: 2.875rem;
}
#bubbles-inner {
//max-width: 700px;
width: 100%;
display: flex;
flex-direction: column;
flex-shrink: 1;
//padding-top: 9px;
margin: 0 auto;
box-sizing: border-box;
min-height: 100%;
justify-content: flex-end;
//flex: 1;
padding: 0 1rem;
max-width: var(--messages-container-width);
@include respond-to(medium-screens) {
width: calc(100% - var(--right-column-width));
}
@include respond-to(handhelds) {
padding: 0 .5rem;
html.is-mac & {
-webkit-user-select: none;
-webkit-touch-callout: none;
}
}
&.is-chat {
.is-in .bubble__container {
@ -891,7 +1001,7 @@ $beside-button-margin: 2.875rem; @@ -891,7 +1001,7 @@ $beside-button-margin: 2.875rem;
//margin-left: 3rem; #DO JS3
@include respond-to(handhelds) {
max-width: calc(100% - #{$handhelds-margin});
max-width: calc(100% - var(--message-handhelds-margin));
}
}
}
@ -903,7 +1013,7 @@ $beside-button-margin: 2.875rem; @@ -903,7 +1013,7 @@ $beside-button-margin: 2.875rem;
}
&.with-beside-button .bubble__container {
max-width: calc(100% - #{$beside-button-margin}) !important;
max-width: calc(100% - var(--message-beside-button-margin)) !important;
}
}
@ -925,7 +1035,6 @@ $beside-button-margin: 2.875rem; @@ -925,7 +1035,6 @@ $beside-button-margin: 2.875rem;
#bubbles-go-down {
position: absolute;
//opacity: 0;
background-color: #fff;
border-radius: 50%;
width: 3.25rem;
@ -936,14 +1045,17 @@ $beside-button-margin: 2.875rem; @@ -936,14 +1045,17 @@ $beside-button-margin: 2.875rem;
align-items: center;
justify-content: center;
right: 17.5px;
bottom: 17.5px;
//bottom: 17.5px;
bottom: 96.5px;
cursor: pointer;
opacity: 0;
transition: .2s opacity;
user-select: none;
z-index: 2;
transition: var(--btn-corner-transition), opacity .2s;
@include respond-to(handhelds) {
bottom: 75.5px;
right: .5rem;
width: 2.875rem;
height: 2.875rem;
}
@ -952,8 +1064,12 @@ $beside-button-margin: 2.875rem; @@ -952,8 +1064,12 @@ $beside-button-margin: 2.875rem;
margin-left: .75px;
}
@include respond-to(handhelds) {
right: .5rem;
@include respond-to(medium-screens) {
transition: transform var(--layer-transition), opacity .2s;
body.is-right-column-shown & {
transform: translate3d(calc(var(--right-column-width) * -1), var(--translateY), 0);
}
}
}

31
src/scss/partials/_chatBubble.scss

@ -41,22 +41,9 @@ $bubble-margin: .25rem; @@ -41,22 +41,9 @@ $bubble-margin: .25rem;
.bubble {
padding-top: $bubble-margin;
max-width: unquote("min(calc(100% - 48px), #{$chat-max-width})");
position: relative;
z-index: 1;
//@include respond-to(not-handhelds) {
margin: 0 auto;
//}
@include respond-to(handhelds) {
max-width: unquote("min(100%, #{$chat-max-width - 16px})");
html.is-mac & {
-webkit-user-select: none;
-webkit-touch-callout: none;
}
}
margin: 0 auto;
&.is-selected {
&:after {
@ -147,7 +134,7 @@ $bubble-margin: .25rem; @@ -147,7 +134,7 @@ $bubble-margin: .25rem;
}
@include respond-to(handhelds) {
max-width: calc(100% - #{$handhelds-margin});
max-width: calc(100% - var(--message-handhelds-margin));
}
> .user-avatar {
@ -169,14 +156,14 @@ $bubble-margin: .25rem; @@ -169,14 +156,14 @@ $bubble-margin: .25rem;
/* &.with-beside-button &__container {
@include respond-to(handhelds) {
max-width: calc(100% - #{$handhelds-margin}) !important;
max-width: calc(100% - var(--message-handhelds-margin)) !important;
}
} */
&.service {
//padding: 1rem 0;
padding: $bubble-margin 0;
max-width: $chat-max-width;
max-width: var(--messages-container-width);
.bubble__container {
/* justify-self: center; */
@ -445,7 +432,7 @@ $bubble-margin: .25rem; @@ -445,7 +432,7 @@ $bubble-margin: .25rem;
align-items: center;
span {
background-color: $time-background;
background-color: var(--message-time-background);
font-size: 23px;
color: #fff;
text-align: center;
@ -886,7 +873,7 @@ $bubble-margin: .25rem; @@ -886,7 +873,7 @@ $bubble-margin: .25rem;
bottom: .1rem;
right: .2rem;
border-radius: 12px;
background-color: $time-background;
background-color: var(--message-time-background);
padding: 0 .2rem;
z-index: 2;
@ -942,7 +929,7 @@ $bubble-margin: .25rem; @@ -942,7 +929,7 @@ $bubble-margin: .25rem;
top: 3px;
left: 3px;
border-radius: 12px;
background-color: $time-background;
background-color: var(--message-time-background);
padding: 0px 6px 0px 6px;
z-index: 2;
font-size: 12px;
@ -954,7 +941,7 @@ $bubble-margin: .25rem; @@ -954,7 +941,7 @@ $bubble-margin: .25rem;
}
span.video-play {
background-color: $time-background;
background-color: var(--message-time-background);
color: #fff;
text-align: center;
font-size: 34px;
@ -1481,7 +1468,7 @@ $bubble-margin: .25rem; @@ -1481,7 +1468,7 @@ $bubble-margin: .25rem;
.bubble-beside-button {
right: auto;
left: -46px;
transform: scaleX(-1);
//transform: scaleX(-1);
}
}

24
src/scss/partials/_chatlist.scss

@ -25,17 +25,23 @@ @@ -25,17 +25,23 @@
}
input {
--border-width: 1px;
background-color: rgba(112, 117, 121, .08);
border: 2px solid transparent;
height: 40px;
border-radius: 22px;
border: var(--border-width) solid transparent;
box-sizing: border-box;
padding: 0px 1.5rem 0 40px;
transition: all .15s ease-out;
padding: 0px calc(1.5rem - var(--border-width)) 0 calc(42px - var(--border-width));
transition: background-color .15s ease-in-out, border-color .15s ease-in-out;
width: 100%;
font-size: 16px;
&:hover {
border-color: var(--color-gray);
}
&:focus {
--border-width: 2px;
background-color: rgba(112, 117, 121, 0);
border: 2px solid $button-primary-background;
@ -86,9 +92,9 @@ @@ -86,9 +92,9 @@
-webkit-user-select: none; /* disable selection/Copy of UIWebView */
-webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */
html.is-mac & {
/* html.is-mac & {
transform: translateZ(0);
}
} */
}
li {
@ -130,13 +136,11 @@ @@ -130,13 +136,11 @@
position: relative;
cursor: pointer;
padding: 9px 8.5px;
margin: 0px 8px 0px 8px;
margin: 0 8px;
overflow: hidden;
@media not all and (min-resolution:.001dpcm)
{ @supports (-webkit-appearance:none) {
margin-right: 3px;
}
html.is-safari & {
margin-right: 3px;
}
@include respond-to(handhelds) {

12
src/scss/partials/_ckin.scss

@ -103,7 +103,7 @@ @@ -103,7 +103,7 @@
visibility: visible;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(1);
transform: translate3d(-50%, -50%, 0) scale(1);
font-size: 64px;
transition: all .2s;
touch-action: manipulation;
@ -170,24 +170,24 @@ @@ -170,24 +170,24 @@
&.is-playing {
.default__gradient-bottom {
transform: translateY(50px);
transform: translate3d(0, 50px, 0);
}
html.no-touch &:hover,
&.show-controls {
.default__gradient-bottom {
transform: translateY(0px);
transform: translateZ(0);
}
.default__controls {
transform: translateY(0);
transform: translateZ(0);
}
}
&:before {
opacity: 0;
visibility: hidden;
transform: translate(-50%, -50%) scale(1.3);
transform: translate3d(-50%, -50%, 0) scale(1.3);
}
.default__button--big {
@ -196,7 +196,7 @@ @@ -196,7 +196,7 @@
}
.default__controls {
transform: translateY(52px);
transform: translate3d(0, 52px, 0);
}
.toggle {

155
src/scss/partials/_document.scss

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
.document {
padding-left: 4.5rem;
height: 70px;
&-ico {
background-color: $color-blue;
border-radius: 5px;
line-height: 10px;
&:after {
content: "";
display: block;
position: absolute;
top: 0;
right: 0;
width: 1.125rem;
height: 1.125rem;
border-bottom-left-radius: .25rem;
border-left: .5625rem solid rgba(0, 0, 0, .25);
border-bottom: .5625rem solid rgba(0, 0, 0, .25);
border-top: .5625rem solid #fff;
border-right: .5625rem solid #fff;
}
}
&-ico, &-download {
font-weight: 500;
letter-spacing: 1px;
font-size: 1.1rem;
background-size: contain;
overflow: hidden;
text-overflow: ellipsis;
}
&-download {
background-color: $color-blue;
border-radius: 8px;
}
&.ext-zip {
.document-ico, .document-download {
background-color: #FB8C00;
}
}
&.ext-pdf {
.document-ico, .document-download {
background-color: #DF3F40;
}
}
&.ext-apk {
.document-ico, .document-download {
background-color: #43A047;
}
}
&:not(.photo) {
.document-ico {
padding-top: 1.5rem;
//background-image: url('../assets/img/doc-in.svg');
}
}
&.photo {
.document-ico {
background: #000;
border-radius: $border-radius;
.document-thumb {
object-fit: cover;
width: 100%;
height: 100%;
}
&:after {
display: none;
}
}
.document-download {
background-color: rgba(0, 0, 0, .15);
}
}
&-name {
white-space: nowrap;
font-weight: 500;
line-height: 1.3;
}
&-size {
white-space: nowrap;
color: $color-gray;
font-size: 14px;
padding-right: 32px;
line-height: 1.3;
}
}
.document, .audio {
display: flex;
flex-direction: column;
justify-content: center;
cursor: pointer;
position: relative;
&-ico, &-download {
position: absolute;
left: 0;
width: 54px;
height: 54px;
color: #fff;
display: flex;
justify-content: center;
&.tgico-largeplay:before {
margin-right: -1px;
}
@include respond-to(handhelds) {
height: 36px;
width: 36px;
}
}
&-download {
z-index: 1;
align-items: center;
font-size: 24px;
cursor: pointer;
.tgico-download {
transform: scale(1);
transition: .2s scale;
}
&.downloading {
.tgico-download {
transform: scale(0);
}
}
}
.preloader-container {
width: 42px;
height: 42px;
@include respond-to(handhelds) {
width: 30px;
height: 30px;
}
}
}

7
src/scss/partials/_emojiDropdown.scss

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
@include respond-to(not-handhelds) {
position: absolute !important;
left: 0;
left: 1rem;
bottom: calc(85px);
width: 420px !important;
height: 420px;
@ -26,6 +26,11 @@ @@ -26,6 +26,11 @@
transform: scale(1);
}
}
@include respond-to(handhelds) {
width: calc(100% + 1rem);
margin-left: -.5rem;
}
> .menu-horizontal {
//font-weight: 500;

75
src/scss/partials/_leftSidebar.scss

@ -1,6 +1,21 @@ @@ -1,6 +1,21 @@
#column-left {
//display: flex;
flex-direction: column;
flex: 1;
min-width: 18rem;
// ! -.5 because of border-left and border-right on whole page
max-width: calc(#{$large-screen} / 4);
@include respond-to(before-medium-screens) {
flex: 2;
}
// ! WARNING, ОЧЕНЬ КРУТОЙ КОСТЫЛЬ - ФИКС ЧЁРНОЙ РАМКИ У КАРТИНОК С ХВОСТИКОМ
html.is-safari & {
@include respond-to(large-screens) {
max-width: calc(#{$large-screen} / 4 - 1.25px);
}
}
.folders-tabs-scrollable {
z-index: 1;
@ -21,12 +36,14 @@ @@ -21,12 +36,14 @@
li {
height: 43px;
padding: 0 16px;
padding: 0 1rem;
display: flex;
justify-content: center;
align-items: center;
flex: 0 0 auto;
min-width: 3rem;
> span:first-child {
> span {
overflow: visible;
i {
@ -83,13 +100,13 @@ @@ -83,13 +100,13 @@
width: 39.75px;
height: 39px;
> * {
> .btn-icon {
visibility: hidden;
opacity: 0;
position: absolute;
left: 0;
top: 0;
transition: .2s opacity;
transition: .2s opacity, .15s background-color;
z-index: 2;
&:before {
@ -250,7 +267,7 @@ @@ -250,7 +267,7 @@
}
#search-container {
transition: 150ms ease-in-out opacity,150ms ease-in-out transform;
transition: .15s ease-in-out opacity, .15s ease-in-out transform;
transform: scale(1.1, 1.1);
opacity: 0;
display: flex;
@ -323,6 +340,7 @@ @@ -323,6 +340,7 @@
line-height: 1.4;
border-radius: 0.625rem;
margin: 0px 0.5rem 0px 0.4375rem;
overflow: hidden;
@include respond-to(handhelds) {
padding: 0.75rem 0.625rem;
@ -448,6 +466,7 @@ @@ -448,6 +466,7 @@
cursor: pointer;
position: relative;
margin-bottom: 10px;
overflow: hidden;
@include respond-to(handhelds) {
padding: 7px 16px 11px 16px;
@ -493,7 +512,7 @@ @@ -493,7 +512,7 @@
padding: 21px 24px 8px 24px;
@include respond-to(handhelds) {
width: 100%;
width: 100%;
padding: 21px 16px 8px 16px;
}
}
@ -558,6 +577,7 @@ @@ -558,6 +577,7 @@
display: flex;
font-size: 1.5rem;
padding: 13px 24px 10px 24px;
overflow: hidden;
@include respond-to(handhelds) {
padding: 13px 16px 10px 16px;
@ -657,71 +677,58 @@ @@ -657,71 +677,58 @@
}
.search-group-recent {
.search-group__name {
display: flex;
justify-content: space-between;
align-items: center;
}
li {
@include respond-to(handhelds) {
padding-top: 0;
}
}
.btn-icon {
//@include respond-to(handhelds) {
font-size: 22px;
//}
}
.user-caption {
@include respond-to(handhelds) {
@include respond-to(handhelds) {
li {
padding-top: 0;
}
.user-caption {
margin-top: -2px;
}
}
p span.user-title {
@include respond-to(handhelds) {
p span.user-title {
font-weight: 500;
}
}
.dialog-avatar {
@include respond-to(handhelds) {
.dialog-avatar {
width: 46px;
height: 46px;
}
}
li > .rp {
@include respond-to(handhelds) {
li > .rp {
height: 62px;
}
}
}
.search-group-recent.search-group.search-group-contacts ul {
@include respond-to(handhelds) {
@include respond-to(handhelds) {
.search-group-recent.search-group.search-group-contacts ul {
margin-top: -2px;
}
}
.search-group.search-group-contacts ul, .search-group.search-group-messages ul {
@include respond-to(handhelds) {
.search-group.search-group-contacts ul, .search-group.search-group-messages ul {
margin-top: 7px;
}
}
.search-group.search-group-messages {
@include respond-to(handhelds) {
.search-group.search-group-messages {
margin-top: -6px;
}
}
.search-group-recent.search-group.search-group-contacts {
@include respond-to(not-handhelds) {
@include respond-to(not-handhelds) {
.search-group-recent.search-group.search-group-contacts {
padding: 0px 0 7px;
}
}

2
src/scss/partials/_mediaViewer.scss

@ -264,7 +264,7 @@ $move-duration: .35s; @@ -264,7 +264,7 @@ $move-duration: .35s;
&.center {
left: 50% !important;
top: 50% !important;
transform: translate(-50%, -50%) !important;
transform: translate3d(-50%, -50%, 0) !important;
@include respond-to(handhelds) {
width: 100% !important;

40
src/scss/partials/_rightSidebar.scss

@ -1,25 +1,37 @@ @@ -1,25 +1,37 @@
#column-right {
position: relative;
@include respond-to(only-small-screens) {
box-shadow: 0 0.25rem 0.5rem 0.1rem hsla(0, 0%, 44.7%, .25);
}
@include respond-to(not-handhelds) {
width: 0%;
transition: .2s ease-in-out;
width: var(--right-column-width);
transition: transform var(--layer-transition);
position: absolute;
right: 0;
z-index: 3;
transform: translate3d(var(--right-column-width), 0, 0);
.sidebar-content {
min-width: 25vw;
@include respond-to(wide-screens) {
min-width: calc(#{$large-screen} / 4 - 1px) !important;
}
min-width: var(--right-column-width);
}
&:not(.active) {
/* &:not(.active) {
border-left-width: 0;
}
} */
&.active {
width: 25%;
body.is-right-column-shown & {
transform: translate3d(0, 0, 0);
}
body.animation-level-0 & {
transition: none;
}
}
@include respond-to(medium-screens) {
border-left: 1px solid #DADCE0;
}
.sidebar-header {
@ -98,7 +110,7 @@ @@ -98,7 +110,7 @@
> .scrollable {
display: flex;
flex-direction: column;
transform: none;
//transform: none;
}
}
@ -300,7 +312,7 @@ @@ -300,7 +312,7 @@
top: 4px;
height: 18px;
border-radius: 4px;
background-color: $time-background;
background-color: var(--message-time-background);
padding: 0px 6px 0px 5px;
z-index: 1;
font-size: 12px;
@ -323,7 +335,7 @@ @@ -323,7 +335,7 @@
}
/* span.video-play {
background-color: $time-background;
background-color: var(--message-time-background);
color: #fff;
text-align: center;
font-size: 34px;

79
src/scss/partials/_ripple.scss

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
.rp {
position: relative;
user-select: none;
}
.rp-overflow, .btn-menu-toggle.rp, .menu-horizontal li.rp/* , html.is-safari .c-ripple */ {
.c-ripple {
width: 100%;
height: 100%;
overflow: hidden;
border-radius: inherit;
&__circle {
overflow: hidden;
}
}
}
/**
* Utility: Ripple
* --------------------------------------------------
*/
.c-ripple {
//display: none !important;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
// ! with these rules ripple lags
/* width: 100%;
height: 100%; */
//overflow: hidden;
/* background: transparent;
border-radius: inherit; */
html.is-safari &:not(:empty) {
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow
border-radius: inherit;
}
&__circle {
background-color: rgba(0, 0, 0, .08);
display: block;
position: absolute;
transform: scale(0);
border-radius: 50%;
animation: ripple-effect .7s forwards;
transition: .35s opacity;
//overflow: hidden;
@include respond-to(handhelds) {
animation-duration: .2s;
transition-duration: .1s;
border-radius: 15%;
}
}
&.is-square .c-ripple__circle, .btn-menu & .c-ripple__circle {
animation-duration: .2s;
transition-duration: .1s;
border-radius: 15%;
}
&__circle.hiding, &__square.hiding {
opacity: 0;
}
}
@keyframes ripple-effect {
0% {
transform: scale(0);
}
to {
transform: scale(2);
}
}

16
src/scss/partials/_scrollable.scss

@ -1,9 +1,9 @@ @@ -1,9 +1,9 @@
div.scrollable::-webkit-scrollbar {
.scrollable::-webkit-scrollbar {
width: 0;
height: 0;
}
div.scrollable::-webkit-scrollbar-thumb {
.scrollable::-webkit-scrollbar-thumb {
width: 0;
height: 0;
}
@ -52,6 +52,10 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -52,6 +52,10 @@ div.scrollable::-webkit-scrollbar-thumb {
overflow-y: overlay;
scrollbar-width: none;
-ms-overflow-style: none;
html.is-safari & {
overflow-y: scroll;
}
}
/* &-sentinel {
@ -112,13 +116,13 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -112,13 +116,13 @@ div.scrollable::-webkit-scrollbar-thumb {
// BROWSER SCROLL
div.scrollable-y::-webkit-scrollbar {
.scrollable-y::-webkit-scrollbar {
width: .375rem;
opacity: 0; // for safari
//height: 200px;
}
div.scrollable:hover::-webkit-scrollbar {
.scrollable:hover::-webkit-scrollbar {
opacity: 1; // for safari
}
@ -132,7 +136,7 @@ div.scrollable:hover::-webkit-scrollbar { @@ -132,7 +136,7 @@ div.scrollable:hover::-webkit-scrollbar {
transition: .2s ease-in-out;
}
div.scrollable:hover::-webkit-scrollbar-thumb {
.scrollable:hover::-webkit-scrollbar-thumb {
height: 200px;
border-radius: $border-radius-medium;
background-color: rgba(0, 0, 0, 0.2);
@ -148,6 +152,6 @@ div.scrollable:hover::-webkit-scrollbar-thumb { @@ -148,6 +152,6 @@ div.scrollable:hover::-webkit-scrollbar-thumb {
background-color: transparent;
}
div.scrollable-x::-webkit-scrollbar {
.scrollable-x::-webkit-scrollbar {
display: none; // for safari iOS
}

11
src/scss/partials/_selector.scss

@ -84,7 +84,10 @@ @@ -84,7 +84,10 @@
margin-right: 8px;
overflow: hidden;
font-size: 14px;
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%);
html.is-safari & {
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow
}
&:after {
position: absolute;
@ -142,10 +145,8 @@ @@ -142,10 +145,8 @@
margin: 0;
}
@media not all and (min-resolution:.001dpcm)
{ @supports (-webkit-appearance:none) {
margin-right: 4px;
}
html.is-safari & {
margin-right: 4px;
}
}
}

14
src/scss/partials/_slider.scss

@ -22,7 +22,9 @@ $slider-time: .25s; @@ -22,7 +22,9 @@ $slider-time: .25s;
padding: .75rem 1rem;
cursor: pointer;
text-align: center;
flex: 1;
flex: 1 1 auto;
//flex: 0 0 auto;
//overflow: hidden;
user-select: none;
// font-size: 1rem;
font-size: 14px;
@ -30,6 +32,15 @@ $slider-time: .25s; @@ -30,6 +32,15 @@ $slider-time: .25s;
position: relative;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
html.no-touch & {
background-color: transparent;
transition: background-color .15s ease-in-out;
&:hover {
background-color: rgba(112, 117, 121, .08);
}
}
> span {
position: relative;
@ -80,6 +91,7 @@ $slider-time: .25s; @@ -80,6 +91,7 @@ $slider-time: .25s;
margin-left: -.25rem;
box-sizing: content-box;
transform-origin: left;
z-index: 1;
&.animate {
transition: transform $slider-time;

9
src/scss/partials/pages/_chats.scss

@ -39,15 +39,10 @@ @@ -39,15 +39,10 @@
}
@include respond-to(not-handhelds) {
#column-left {
width: 25%;
}
#column-center {
display: flex!important;
background-color: #e6ebee;
width: 100%;
flex: 1;
}
#column-right {
@ -74,7 +69,7 @@ @@ -74,7 +69,7 @@
position: absolute;
top: 50%;
left: 50%;
transform: translateY(-50%) translateX(-50%);
transform: translate3d(-50%, -50%, 0);
&-path {
stroke: $button-primary-background;
@ -94,7 +89,7 @@ @@ -94,7 +89,7 @@
background: #fff;
}
@include respond-to(wide-screens) {
@include respond-to(large-screens) {
border-top-width: 0;
border-bottom-width: 0;
border-left-width: 1px;

5
src/scss/partials/pages/_pages.scss

@ -27,13 +27,8 @@ @@ -27,13 +27,8 @@
.tabs-container {
height: 100%;
transform: translateX(0);
width: 100%;
&.animated {
transition: .42s transform;
}
> div {
/* justify-content: center; */
/* &.active {

4
src/scss/partials/popups/_popup.scss

@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
z-index: 4;
.popup-container {
transform: translateY(0);
transform: translate3d(0, 0, 0);
}
}
@ -36,7 +36,7 @@ @@ -36,7 +36,7 @@
border-radius: $border-radius-medium;
background-color: #fff;
padding: 1rem;
transform: translateY(-40px);
transform: translate3d(0, -40px, 0);
backface-visibility: hidden;
transition-property: transform;
transition-duration: 0.3s;

511
src/scss/style.scss

@ -19,6 +19,7 @@ $light: rgba($color-gray, 0.08); @@ -19,6 +19,7 @@ $light: rgba($color-gray, 0.08);
//$small-screen: 720px;
$small-screen: 896px;
//$small-screen: 900px;
$medium-screen: 1275px;
$large-screen: 1680px;
//$large-screen: 16800px;
@ -27,10 +28,22 @@ $large-screen: 1680px; @@ -27,10 +28,22 @@ $large-screen: 1680px;
@media only screen and (max-width: $small-screen) { @content; }
//@media only screen and (orientation: landscape) and (max-device-width: 896px) { @content; } // iPhone 11 Pro Max
}
@else if $media == small-screens {
@media only screen and (min-width: $small-screen + 1) { @content; }
}
@else if $media == only-small-screens {
@media only screen and (min-width: $small-screen + 1) and (max-width: $medium-screen) { @content; }
}
@else if $media == medium-screens {
@media only screen and (min-width: $small-screen + 1) and (max-width: $large-screen) { @content; }
@media only screen and (min-width: $medium-screen + 1) { @content; }
}
@else if $media == only-medium-screens {
@media only screen and (min-width: $medium-screen + 1) and (max-width: $large-screen) { @content; }
}
@else if $media == wide-screens {
@else if $media == before-medium-screens {
@media only screen and (max-width: $medium-screen) { @content; }
}
@else if $media == large-screens {
@media only screen and (min-width: $large-screen + 1) { @content; }
}
@else if $media == not-handhelds {
@ -38,6 +51,30 @@ $large-screen: 1680px; @@ -38,6 +51,30 @@ $large-screen: 1680px;
}
}
:root {
--color-gray: #c4c9cc;
--layer-transition: .2s ease-in-out;
--btn-corner-transition: transform .2s cubic-bezier(.34, 1.56, .64, 1);
//--layer-transition: .3s cubic-bezier(.33, 1, .68, 1);
//--layer-transition: none;
--message-handhelds-margin: 5.5625rem;
--message-beside-button-margin: 2.875rem;
--message-time-background: rgba(0, 0, 0, .35);
--messages-container-width: 728px;
@include respond-to(handhelds) {
--right-column-width: 100vw;
}
@include respond-to(not-handhelds) {
--right-column-width: calc(#{$large-screen} / 4);
}
@include respond-to(only-medium-screens) {
--right-column-width: 25vw;
}
}
@import "partials/ico";
@import "partials/chatlist";
@import "partials/chat";
@ -53,6 +90,10 @@ $large-screen: 1680px; @@ -53,6 +90,10 @@ $large-screen: 1680px;
@import "partials/selector";
@import "partials/gifsMasonry";
@import "partials/preloader";
@import "partials/ripple";
@import "partials/avatar";
@import "partials/document";
@import "partials/audio";
@import "partials/popups/popup";
@import "partials/popups/editAvatar";
@ -237,15 +278,14 @@ input, textarea { @@ -237,15 +278,14 @@ input, textarea {
font-size: 1.5rem;
line-height: 1.5rem;
border-radius: 50%;
-webkit-transition: background-color .15s ease-out;
transition: background-color .15s ease-out;
transition: background-color .15s ease-in-out;
color: $color-gray;
cursor: pointer;
background: none;
background-color: transparent;
border: none;
padding: .5rem;
position: relative;
/* overflow: hidden; */
overflow: hidden;
/* kostil */
display: flex;
@ -257,7 +297,7 @@ input, textarea { @@ -257,7 +297,7 @@ input, textarea {
}
html.no-touch &:hover {
background-color: rgba(112, 117, 121, 0.08);
background-color: rgba(112, 117, 121, .08);
}
}
@ -266,16 +306,22 @@ input, textarea { @@ -266,16 +306,22 @@ input, textarea {
}
.btn-corner {
--translateY: calc(100% + 20px);
position: absolute !important;
bottom: 20px;
right: 20px;
transition: .2s ease;
transform: translateY(calc(100% + 20px));
//transition: .2s ease;
transition: var(--btn-corner-transition);
transform: translate3d(0, var(--translateY), 0);
z-index: 3;
&.is-visible {
transform: translateY(0px);
--translateY: 0;
}
body.animation-level-0 & {
transition: none !important;
}
}
.danger {
@ -429,66 +475,6 @@ hr { @@ -429,66 +475,6 @@ hr {
margin: 0 0 8px;
}
avatar-element {
color: #fff;
width: 54px;
height: 54px;
line-height: 54px;
border-radius: 50%;
background-color: $color-blue;
text-align: center;
font-size: 1.25em;
/* overflow: hidden; */
position: relative;
user-select: none;
@include respond-to(handhelds) {
font-size: 14px;
}
/* kostil */
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
border-radius: inherit;
user-select: none;
&.fade-in {
animation: fadeIn .2s ease forwards;
}
}
&[class*=" tgico-"] {
line-height: 52px;
font-size: 28px;
}
path {
fill: white;
}
&.is-online:after {
position: absolute;
content: " ";
display: block;
border-radius: 50%;
border: 2px solid white;
background-color: #0ac630;
left: 74%;
top: 73%;
width: 14px;
height: 14px;
}
&.tgico-avatar_deletedaccount {
font-size: 3rem;
}
}
.user-title, b/* , .user-last-message b */ {
color: #000;
font-weight: 500;
@ -499,305 +485,6 @@ avatar-element { @@ -499,305 +485,6 @@ avatar-element {
font-weight: 400;
}
.rp {
position: relative;
user-select: none;
}
/**
* Utility: Ripple
* --------------------------------------------------
*/
.c-ripple {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: transparent;
border-radius: inherit;
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow
&__circle {
background-color: rgba(0,0,0,.08);
display: block;
position: absolute;
transform: scale(0);
border-radius: 50%;
animation: ripple-effect .7s forwards;
transition: .35s opacity;
overflow: hidden;
@include respond-to(handhelds) {
animation-duration: .2s;
transition-duration: .1s;
border-radius: 15%;
}
}
&.is-square .c-ripple__circle, .btn-menu & .c-ripple__circle {
animation-duration: .2s;
transition-duration: .1s;
border-radius: 15%;
}
&__circle.hiding, &__square.hiding {
opacity: 0;
}
}
@keyframes ripple-effect {
0% {
transform: scale(0);
}
to {
transform: scale(2);
}
}
.document {
padding-left: 4.5rem;
height: 70px;
&-ico {
background-color: $color-blue;
border-radius: 5px;
line-height: 10px;
&:after {
content: "";
display: block;
position: absolute;
top: 0;
right: 0;
width: 1.125rem;
height: 1.125rem;
border-bottom-left-radius: .25rem;
border-left: .5625rem solid rgba(0, 0, 0, .25);
border-bottom: .5625rem solid rgba(0, 0, 0, .25);
border-top: .5625rem solid #fff;
border-right: .5625rem solid #fff;
}
}
&-ico, &-download {
font-weight: 500;
letter-spacing: 1px;
font-size: 1.1rem;
background-size: contain;
overflow: hidden;
text-overflow: ellipsis;
}
&-download {
background-color: $color-blue;
border-radius: 8px;
}
&.ext-zip {
.document-ico, .document-download {
background-color: #FB8C00;
}
}
&.ext-pdf {
.document-ico, .document-download {
background-color: #DF3F40;
}
}
&.ext-apk {
.document-ico, .document-download {
background-color: #43A047;
}
}
&:not(.photo) {
.document-ico {
padding-top: 1.5rem;
//background-image: url('../assets/img/doc-in.svg');
}
}
&.photo {
.document-ico {
background: #000;
border-radius: $border-radius;
.document-thumb {
object-fit: cover;
width: 100%;
height: 100%;
}
&:after {
display: none;
}
}
.document-download {
background-color: rgba(0, 0, 0, .15);
}
}
&-name {
white-space: nowrap;
font-weight: 500;
line-height: 1.3;
}
&-size {
white-space: nowrap;
color: $color-gray;
font-size: 14px;
padding-right: 32px;
line-height: 1.3;
}
}
.document, .audio {
display: flex;
flex-direction: column;
justify-content: center;
cursor: pointer;
position: relative;
&-ico, &-download {
position: absolute;
left: 0;
width: 54px;
height: 54px;
color: #fff;
display: flex;
justify-content: center;
&.tgico-largeplay:before {
margin-right: -1px;
}
@include respond-to(handhelds) {
height: 36px;
width: 36px;
}
}
&-download {
z-index: 1;
align-items: center;
font-size: 24px;
cursor: pointer;
.tgico-download {
transform: scale(1);
transition: .2s scale;
}
&.downloading {
.tgico-download {
transform: scale(0);
}
}
}
.preloader-container {
width: 42px;
height: 42px;
@include respond-to(handhelds) {
width: 30px;
height: 30px;
}
}
}
.audio {
position: relative;
padding-left: 67px;
min-height: 58px;
max-width: 244px;
overflow: visible!important;
@include respond-to(handhelds) {
padding-left: 45px;
}
&-toggle, &-download {
border-radius: 50%;
background-color: $color-blue;
font-size: 2.3rem;
align-items: center;
@include respond-to(handhelds) {
font-size: 24px !important;
}
}
&-download {
z-index: 2;
}
&-waveform {
height: 23px;
//overflow: visible!important;
rect {
//overflow: visible!important;
fill: #CBCBCB;
&.active {
fill: $color-blue;
}
}
}
&-title {
font-size: 1rem;
color: #000;
user-select: none;
}
&-time, &-subtitle {
font-size: 14px;
color: $color-gray;
margin-top: 3px;
margin-left: -1px;
user-select: none;
@include respond-to(handhelds) {
margin-top: 1px;
font-size: 12px;
}
}
&-title, &:not(.audio-show-progress) &-subtitle {
white-space: nowrap;
overflow: hidden;
max-width: 100%;
text-overflow: ellipsis;
}
@include respond-to(handhelds) {
&-download {
/* background: transparent; */
margin-left: 2px;
margin-top: 1px;
}
&.is-voice {
.audio-download {
margin: 0;
}
}
}
}
.avatar-edit {
position: relative;
border-radius: 50%;
@ -892,6 +579,12 @@ avatar-element { @@ -892,6 +579,12 @@ avatar-element {
position: relative;
z-index: 1;
html.no-touch & {
&:hover {
border-color: var(--color-gray);
}
}
@include respond-to(handhelds) {
height: 50px;
}
@ -902,7 +595,7 @@ avatar-element { @@ -902,7 +595,7 @@ avatar-element {
} */
&:focus {
border-color: $button-primary-background;
border-color: $button-primary-background !important;
border-width: 2px;
padding: 0 calc(1rem - 1px);
}
@ -1377,84 +1070,6 @@ img.emoji { @@ -1377,84 +1070,6 @@ img.emoji {
}
}
/* .fade-in-end {
opacity: 1;
transition: opacity .2s ease;
}
.fade-in-reverse {
opacity: 0 !important;
transition: 0s !important;
} */
/* #chats-container {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
-moz-box-orient: vertical;
-moz-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
height: 100%;
.scrollable {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
-moz-box-orient: vertical;
-moz-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
-moz-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
height: 1px;
position: relative;
overflow: auto;
-webkit-overflow-scrolling: touch;
-webkit-transform: translateZ(0px);
-moz-transform: translateZ(0px);
transform: translateZ(0px);
> div {
height: 100%;
position: relative;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
-moz-box-orient: vertical;
-moz-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
-moz-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
height: 1px;
}
}
} */
.chats-container #chats-container .scrollable #dialogs .rp avatar-element {
font-size: 20px;

4
src/types.d.ts vendored

@ -24,3 +24,7 @@ export type WorkerTaskTemplate = { @@ -24,3 +24,7 @@ export type WorkerTaskTemplate = {
};
export type Modify<T, R> = Omit<T, keyof R> & R;
//export type Parameters<T> = T extends (... args: infer T) => any ? T : never;
export type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never;

462
src/vendor/smoothscroll.ts vendored

@ -0,0 +1,462 @@ @@ -0,0 +1,462 @@
// credits to https://github.com/iamdustan/smoothscroll
type ScrollableElement = (Window & typeof globalThis) | Element;
export type SmoothScrollToOptions = Partial<{
top: number,
left: number,
behavior: 'smooth' | 'auto' | 'instant',
scrollTime: number
}>;
export const SCROLL_TIME = 468;
// polyfill
export default function polyfill() {
// aliases
var w = window;
var d = document;
// return if scroll behavior is supported and polyfill is not forced
if(
'scrollBehavior' in d.documentElement.style &&
(w as any).__forceSmoothScrollPolyfill__ !== true
) {
return;
}
// globals
var Element = w.HTMLElement || w.Element;
// object gathering original scroll methods
var original = {
scroll: w.scroll || w.scrollTo,
scrollBy: w.scrollBy,
elementScroll: Element.prototype.scroll || scrollElement,
scrollIntoView: Element.prototype.scrollIntoView
};
// define timing method
var now =
w.performance && w.performance.now
? w.performance.now.bind(w.performance)
: Date.now;
/**
* indicates if a the current browser is made by Microsoft
* @method isMicrosoftBrowser
* @param {String} userAgent
* @returns {Boolean}
*/
function isMicrosoftBrowser(userAgent: string) {
var userAgentPatterns = ['MSIE ', 'Trident/', 'Edge/'];
return new RegExp(userAgentPatterns.join('|')).test(userAgent);
}
/*
* IE has rounding bug rounding down clientHeight and clientWidth and
* rounding up scrollHeight and scrollWidth causing false positives
* on hasScrollableSpace
*/
var ROUNDING_TOLERANCE = isMicrosoftBrowser(w.navigator.userAgent) ? 1 : 0;
/**
* changes scroll position inside an element
* @method scrollElement
* @param {Number} x
* @param {Number} y
* @returns {undefined}
*/
function scrollElement(this: Element, x: number, y: number) {
this.scrollLeft = x;
this.scrollTop = y;
}
/**
* returns result of applying ease math function to a number
* @method ease
* @param {Number} k
* @returns {Number}
*/
function ease(k: number) {
return 0.5 * (1 - Math.cos(Math.PI * k));
}
/**
* indicates if a smooth behavior should be applied
* @method shouldBailOut
* @param {Number|Object} firstArg
* @returns {Boolean}
*/
function shouldBailOut(firstArg: SmoothScrollToOptions) {
if(
firstArg === null ||
typeof firstArg !== 'object' ||
firstArg.behavior === undefined ||
firstArg.behavior === 'auto' ||
firstArg.behavior === 'instant'
) {
// first argument is not an object/null
// or behavior is auto, instant or undefined
return true;
}
if(typeof firstArg === 'object' && firstArg.behavior === 'smooth') {
// first argument is an object and behavior is smooth
return false;
}
// throw error when behavior is not supported
throw new TypeError(
'behavior member of ScrollOptions ' +
firstArg.behavior +
' is not a valid value for enumeration ScrollBehavior.'
);
}
/**
* indicates if an element has scrollable space in the provided axis
* @method hasScrollableSpace
* @param {Node} el
* @param {String} axis
* @returns {Boolean}
*/
function hasScrollableSpace(el: Element, axis: 'X' | 'Y') {
if(axis === 'Y') {
return el.clientHeight + ROUNDING_TOLERANCE < el.scrollHeight;
}
if(axis === 'X') {
return el.clientWidth + ROUNDING_TOLERANCE < el.scrollWidth;
}
}
/**
* indicates if an element has a scrollable overflow property in the axis
* @method canOverflow
* @param {Node} el
* @param {String} axis
* @returns {Boolean}
*/
function canOverflow(el: Element, axis: string) {
// @ts-ignore
var overflowValue: string = w.getComputedStyle(el, null)['overflow' + axis];
return overflowValue === 'auto' || overflowValue === 'scroll';
}
/**
* indicates if an element can be scrolled in either axis
* @method isScrollable
* @param {Node} el
* @param {String} axis
* @returns {Boolean}
*/
function isScrollable(el: Element) {
var isScrollableY = hasScrollableSpace(el, 'Y') && canOverflow(el, 'Y');
var isScrollableX = hasScrollableSpace(el, 'X') && canOverflow(el, 'X');
return isScrollableY || isScrollableX;
}
/**
* finds scrollable parent of an element
* @method findScrollableParent
* @param {Node} el
* @returns {Node} el
*/
function findScrollableParent(el: Element) {
while(el !== d.body && isScrollable(el) === false) {
// @ts-ignore
el = el.parentNode || el.host;
}
return el;
}
/**
* self invoked function that, given a context, steps through scrolling
* @method step
* @param {Object} context
* @returns {undefined}
*/
function step(context: {
startTime: number,
scrollTime: number,
startX: number,
startY: number,
x: number,
y: number,
scrollable: ScrollableElement,
method: (this: ScrollableElement, currentX: number, currentY: number) => any
}) {
var time = now();
var value: number;
var currentX: number;
var currentY: number;
var elapsed = (time - context.startTime) / context.scrollTime;
// avoid elapsed times higher than one
elapsed = elapsed > 1 ? 1 : elapsed;
// apply easing to elapsed time
value = ease(elapsed);
currentX = context.startX + (context.x - context.startX) * value;
currentY = context.startY + (context.y - context.startY) * value;
context.method.call(context.scrollable, currentX, currentY);
// scroll more if we have not reached our destination
if(currentX !== context.x || currentY !== context.y) {
w.requestAnimationFrame(step.bind(w, context));
}
}
/**
* scrolls window or element with a smooth behavior
* @method smoothScroll
* @param {Object|Node} el
* @param {Number} x
* @param {Number} y
* @returns {undefined}
*/
function smoothScroll(el: Element, x: number, y: number, scrollTime = SCROLL_TIME) {
var scrollable: ScrollableElement;
var startX: number;
var startY: number;
var method: any;
var startTime = now();
// define scroll context
if(el === d.body) {
scrollable = w;
startX = w.scrollX || w.pageXOffset;
startY = w.scrollY || w.pageYOffset;
method = original.scroll;
} else {
scrollable = el;
startX = el.scrollLeft;
startY = el.scrollTop;
method = scrollElement;
}
// scroll looping over a frame
step({
scrollable: scrollable,
method: method,
scrollTime,
startTime: startTime,
startX: startX,
startY: startY,
x: x,
y: y
});
}
// ORIGINAL METHODS OVERRIDES
// w.scroll and w.scrollTo
w.scroll = w.scrollTo = function() {
const options = arguments[0] as SmoothScrollToOptions;
// avoid action when no arguments are passed
if(options === undefined) {
return;
}
// avoid smooth behavior if not required
if(shouldBailOut(options) === true) {
original.scroll.call(
w,
options.left !== undefined
? options.left
: typeof options !== 'object'
? options
: w.scrollX || w.pageXOffset,
// use top prop, second argument if present or fallback to scrollY
options.top !== undefined
? options.top
: arguments[1] !== undefined
? arguments[1]
: w.scrollY || w.pageYOffset
);
return;
}
// LET THE SMOOTHNESS BEGIN!
smoothScroll.call(
w,
d.body,
options.left !== undefined
? ~~options.left
: w.scrollX || w.pageXOffset,
options.top !== undefined
? ~~options.top
: w.scrollY || w.pageYOffset,
options.scrollTime
);
};
// w.scrollBy
w.scrollBy = function() {
const options = arguments[0] as SmoothScrollToOptions;
// avoid action when no arguments are passed
if(options === undefined) {
return;
}
// avoid smooth behavior if not required
if(shouldBailOut(options)) {
original.scrollBy.call(
w,
options.left !== undefined
? options.left
: typeof options !== 'object' ? options : 0,
options.top !== undefined
? options.top
: arguments[1] !== undefined ? arguments[1] : 0
);
return;
}
// LET THE SMOOTHNESS BEGIN!
smoothScroll.call(
w,
d.body,
~~options.left + (w.scrollX || w.pageXOffset),
~~options.top + (w.scrollY || w.pageYOffset),
options.scrollTime
);
};
// Element.prototype.scroll and Element.prototype.scrollTo
Element.prototype.scroll = Element.prototype.scrollTo = function() {
const options = arguments[0] as SmoothScrollToOptions;
// avoid action when no arguments are passed
if(options === undefined) {
return;
}
// avoid smooth behavior if not required
if(shouldBailOut(options) === true) {
// if one number is passed, throw error to match Firefox implementation
if(typeof options === 'number' && arguments[1] === undefined) {
throw new SyntaxError('Value could not be converted');
}
original.elementScroll.call(
this,
// use left prop, first number argument or fallback to scrollLeft
options.left !== undefined
? ~~options.left
: typeof options !== 'object' ? ~~options : this.scrollLeft,
// use top prop, second argument or fallback to scrollTop
options.top !== undefined
? ~~options.top
: arguments[1] !== undefined ? ~~arguments[1] : this.scrollTop
);
return;
}
var left = options.left;
var top = options.top;
// LET THE SMOOTHNESS BEGIN!
smoothScroll.call(
this,
this,
typeof left === 'undefined' ? this.scrollLeft : ~~left,
typeof top === 'undefined' ? this.scrollTop : ~~top,
options.scrollTime
);
};
// Element.prototype.scrollBy
Element.prototype.scrollBy = function() {
const options = arguments[0] as SmoothScrollToOptions;
// avoid action when no arguments are passed
if(options === undefined) {
return;
}
// avoid smooth behavior if not required
if(shouldBailOut(options) === true) {
original.elementScroll.call(
this,
options.left !== undefined
? ~~options.left + this.scrollLeft
: ~~options + this.scrollLeft,
options.top !== undefined
? ~~options.top + this.scrollTop
: ~~arguments[1] + this.scrollTop
);
return;
}
this.scroll({
left: ~~options.left + this.scrollLeft,
top: ~~options.top + this.scrollTop,
behavior: options.behavior as any,
scrollTime: options.scrollTime
} as any);
};
// Element.prototype.scrollIntoView
Element.prototype.scrollIntoView = function() {
const options = arguments[0] as SmoothScrollToOptions;
// avoid smooth behavior if not required
if(shouldBailOut(options) === true) {
original.scrollIntoView.call(
this,
(options === undefined ? true : options) as any
);
return;
}
// LET THE SMOOTHNESS BEGIN!
var scrollableParent = findScrollableParent(this);
var parentRects = scrollableParent.getBoundingClientRect();
var clientRects = this.getBoundingClientRect();
if(scrollableParent !== d.body) {
// reveal element inside parent
smoothScroll.call(
this,
scrollableParent,
scrollableParent.scrollLeft + clientRects.left - parentRects.left,
scrollableParent.scrollTop + clientRects.top - parentRects.top,
options.scrollTime
);
// reveal parent in viewport unless is fixed
if(w.getComputedStyle(scrollableParent).position !== 'fixed') {
w.scrollBy({
left: parentRects.left,
top: parentRects.top,
behavior: 'smooth',
scrollTime: options.scrollTime
} as any);
}
} else {
// reveal element in viewport
w.scrollBy({
left: clientRects.left,
top: clientRects.top,
behavior: 'smooth',
scrollTime: options.scrollTime
} as any);
}
};
}
/* if (typeof exports === 'object' && typeof module !== 'undefined') {
// commonjs
module.exports = { polyfill: polyfill };
} else {
// global
polyfill();
} */

0
src/vendor/smoothscroll.js → src/vendor/smoothscroll_minified.js vendored

46763
stats.json

File diff suppressed because one or more lines are too long

BIN
whybundled.txt

Binary file not shown.
Loading…
Cancel
Save