Browse Source

Shared media sticky header

Shared media close icon animation
New left sidebar icon menu animation
Debounce opening right sidebar
Fix chat input overflow when multiselecting
Fix chat input height when ESG doesn't fit
master
Eduard Kuzmenko 4 years ago
parent
commit
2d6d47f7e8
  1. 4
      src/components/appSearchSuper..ts
  2. 4
      src/components/horizontalMenu.ts
  3. 9
      src/components/scrollable.ts
  4. 2
      src/components/sidebarLeft/index.ts
  5. 2
      src/components/sidebarRight/index.ts
  6. 42
      src/components/sidebarRight/tabs/sharedMedia.ts
  7. 3
      src/components/slider.ts
  8. 30
      src/components/transition.ts
  9. 4
      src/helpers/blur.ts
  10. 2
      src/helpers/fastSmoothScroll.ts
  11. 22
      src/index.hbs
  12. 8
      src/lib/appManagers/appImManager.ts
  13. 38
      src/lib/mtproto/networker.ts
  14. 81
      src/scss/partials/_animatedIcon.scss
  15. 15
      src/scss/partials/_chat.scss
  16. 39
      src/scss/partials/_leftSidebar.scss
  17. 13
      src/scss/partials/_rightSidebar.scss
  18. 100
      src/scss/style.scss

4
src/components/appSearchSuper..ts

@ -80,7 +80,7 @@ export default class AppSearchSuper {
private searchGroupMedia: SearchGroup; private searchGroupMedia: SearchGroup;
constructor(public types: {inputFilter: SearchSuperType, name: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false) { constructor(public types: {inputFilter: SearchSuperType, name: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false, public groupByMonth = true) {
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add('search-super'); this.container.classList.add('search-super');
@ -549,7 +549,7 @@ export default class AppSearchSuper {
const method = append ? 'append' : 'prepend'; const method = append ? 'append' : 'prepend';
elemsToAppend.forEach(details => { elemsToAppend.forEach(details => {
const {element, message} = details; const {element, message} = details;
const monthContainer = this.getMonthContainerByTimestamp(message.date, type); const monthContainer = this.getMonthContainerByTimestamp(this.groupByMonth ? message.date : 0, type);
element.classList.add('search-super-item'); element.classList.add('search-super-item');
element.dataset.mid = '' + message.mid; element.dataset.mid = '' + message.mid;
element.dataset.peerId = '' + message.peerId; element.dataset.peerId = '' + message.peerId;

4
src/components/horizontalMenu.ts

@ -1,4 +1,4 @@
import { findUpTag, whichChild, findUpAsChild } from "../helpers/dom"; import { whichChild, findUpAsChild } from "../helpers/dom";
import { TransitionSlider } from "./transition"; import { TransitionSlider } from "./transition";
import { ScrollableX } from "./scrollable"; import { ScrollableX } from "./scrollable";
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
@ -105,4 +105,4 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
} }
return selectTab; return selectTab;
} }

9
src/components/scrollable.ts

@ -52,6 +52,7 @@ export class ScrollableBase {
protected onScroll: () => void; protected onScroll: () => void;
public isHeavyAnimationInProgress = false; public isHeavyAnimationInProgress = false;
protected needCheckAfterAnimation = false;
constructor(public el: HTMLElement, logPrefix = '', public container: HTMLElement = document.createElement('div')) { constructor(public el: HTMLElement, logPrefix = '', public container: HTMLElement = document.createElement('div')) {
this.container.classList.add('scrollable'); this.container.classList.add('scrollable');
@ -74,11 +75,16 @@ export class ScrollableBase {
this.isHeavyAnimationInProgress = true; this.isHeavyAnimationInProgress = true;
if(this.onScrollMeasure) { if(this.onScrollMeasure) {
this.needCheckAfterAnimation = true;
window.cancelAnimationFrame(this.onScrollMeasure); window.cancelAnimationFrame(this.onScrollMeasure);
} }
}, () => { }, () => {
this.isHeavyAnimationInProgress = false; this.isHeavyAnimationInProgress = false;
this.onScroll();
if(this.needCheckAfterAnimation) {
this.onScroll();
this.needCheckAfterAnimation = false;
}
}); });
} }
@ -146,6 +152,7 @@ export default class Scrollable extends ScrollableBase {
window.cancelAnimationFrame(this.onScrollMeasure); window.cancelAnimationFrame(this.onScrollMeasure);
} }
this.needCheckAfterAnimation = true;
return; return;
} }

2
src/components/sidebarLeft/index.ts

@ -418,6 +418,7 @@ export class AppSidebarLeft extends SidebarSlider {
this.toolsBtn.classList.remove('active'); this.toolsBtn.classList.remove('active');
this.backBtn.classList.add('active'); this.backBtn.classList.add('active');
this.newBtnMenu.classList.add('is-hidden'); this.newBtnMenu.classList.add('is-hidden');
this.toolsBtn.parentElement.firstElementChild.classList.toggle('state-back', true);
transition(1); transition(1);
}; };
@ -428,6 +429,7 @@ export class AppSidebarLeft extends SidebarSlider {
this.backBtn.addEventListener('click', (e) => { this.backBtn.addEventListener('click', (e) => {
this.toolsBtn.classList.add('active'); this.toolsBtn.classList.add('active');
this.backBtn.classList.remove('active'); this.backBtn.classList.remove('active');
this.toolsBtn.parentElement.firstElementChild.classList.toggle('state-back', false);
transition(0); transition(0);
}); });

2
src/components/sidebarRight/index.ts

@ -103,7 +103,7 @@ export class AppSidebarRight extends SidebarSlider {
this.selectTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia); this.selectTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia);
} }
const transitionTime = rootScope.settings.animationsEnabled ? mediaSizes.isMobile ? 250 : 200 : 0; const transitionTime = rootScope.settings.animationsEnabled ? (mediaSizes.isMobile ? 250 : 200) : 0;
const promise = pause(transitionTime); const promise = pause(transitionTime);
if(transitionTime) { if(transitionTime) {
dispatchHeavyAnimationEvent(promise, transitionTime); dispatchHeavyAnimationEvent(promise, transitionTime);

42
src/components/sidebarRight/tabs/sharedMedia.ts

@ -11,6 +11,9 @@ import AvatarElement from "../../avatar";
import Scrollable from "../../scrollable"; import Scrollable from "../../scrollable";
import { SliderTab } from "../../slider"; import { SliderTab } from "../../slider";
import CheckboxField from "../../checkbox"; import CheckboxField from "../../checkbox";
import { attachClickEvent, cancelEvent } from "../../../helpers/dom";
import appSidebarRight from "..";
import { TransitionSlider } from "../../transition";
let setText = (text: string, el: HTMLDivElement) => { let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
@ -29,7 +32,7 @@ let setText = (text: string, el: HTMLDivElement) => {
// TODO: отредактированное сообщение не изменится // TODO: отредактированное сообщение не изменится
export default class AppSharedMediaTab implements SliderTab { export default class AppSharedMediaTab implements SliderTab {
public container: HTMLElement; public container: HTMLElement;
public closeBtn: HTMLElement; public closeBtn: HTMLButtonElement;
private peerId = 0; private peerId = 0;
private threadId = 0; private threadId = 0;
@ -62,7 +65,8 @@ export default class AppSharedMediaTab implements SliderTab {
public init() { public init() {
this.container = document.getElementById('shared-media-container'); this.container = document.getElementById('shared-media-container');
this.closeBtn = this.container.querySelector('.sidebar-close-button'); this.closeBtn = this.container.querySelector('.sidebar-header .btn-icon');
this.closeBtn.classList.add('sidebar-close-button');
this.profileContentEl = this.container.querySelector('.profile-content'); this.profileContentEl = this.container.querySelector('.profile-content');
this.profileElements = { this.profileElements = {
@ -86,7 +90,37 @@ export default class AppSharedMediaTab implements SliderTab {
this.profileElements.notificationsRow.prepend(checkboxField.label); this.profileElements.notificationsRow.prepend(checkboxField.label);
this.scroll = new Scrollable(this.container, 'SR', 400); this.scroll = new Scrollable(this.container, 'SR', 400);
const HEADER_HEIGHT = 56;
const closeIcon = this.closeBtn.firstElementChild as HTMLElement;
this.scroll.onAdditionalScroll = () => {
const rect = this.searchSuper.nav.getBoundingClientRect();
if(!rect.width) return;
//console.log('daddy issues', this.searchSuper.nav.getBoundingClientRect());
const top = rect.top;
const isSharedMedia = top <= HEADER_HEIGHT;
closeIcon.classList.toggle('state-back', isSharedMedia);
transition(+isSharedMedia);
};
const transition = TransitionSlider(this.closeBtn.nextElementSibling as HTMLElement, 'slide-fade', 400, null, false);
transition(0);
attachClickEvent(this.closeBtn, (e) => {
if(this.closeBtn.firstElementChild.classList.contains('state-back')) {
this.scroll.scrollIntoViewNew(this.scroll.container.firstElementChild as HTMLElement, 'start');
transition(0);
closeIcon.classList.remove('state-back');
} else if(!this.scroll.isHeavyAnimationInProgress) {
appSidebarRight.closeTab();
}
});
this.container.prepend(this.closeBtn.parentElement);
this.profileElements.notificationsCheckbox.addEventListener('change', () => { this.profileElements.notificationsCheckbox.addEventListener('change', () => {
//let checked = this.profileElements.notificationsCheckbox.checked; //let checked = this.profileElements.notificationsCheckbox.checked;
appMessagesManager.mutePeer(this.peerId); appMessagesManager.mutePeer(this.peerId);
@ -134,7 +168,7 @@ export default class AppSharedMediaTab implements SliderTab {
}, { }, {
inputFilter: 'inputMessagesFilterMusic', inputFilter: 'inputMessagesFilterMusic',
name: 'Music' name: 'Music'
}], this.scroll); }], this.scroll/* , undefined, undefined, false */);
this.profileContentEl.append(this.searchSuper.container); this.profileContentEl.append(this.searchSuper.container);
} }

3
src/components/slider.ts

@ -2,6 +2,7 @@ import { attachClickEvent } from "../helpers/dom";
import { horizontalMenu } from "./horizontalMenu"; import { horizontalMenu } from "./horizontalMenu";
import ButtonIcon from "./buttonIcon"; import ButtonIcon from "./buttonIcon";
import Scrollable from "./scrollable"; import Scrollable from "./scrollable";
import { TransitionSlider } from "./transition";
export interface SliderTab { export interface SliderTab {
onOpen?: () => void, onOpen?: () => void,
@ -86,7 +87,7 @@ export default class SidebarSlider {
constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab} = {}, private canHideFirst = false) { constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab} = {}, private canHideFirst = false) {
this.tabsContainer = this.sidebarEl.querySelector('.sidebar-slider'); this.tabsContainer = this.sidebarEl.querySelector('.sidebar-slider');
this._selectTab = horizontalMenu(null, this.tabsContainer as HTMLDivElement, null, null, TRANSITION_TIME); this._selectTab = TransitionSlider(this.tabsContainer, 'navigation', TRANSITION_TIME);
if(!canHideFirst) { if(!canHideFirst) {
this._selectTab(0); this._selectTab(0);
} }

30
src/components/transition.ts

@ -46,7 +46,7 @@ function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight
}; };
} }
export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fade' | 'none'/* | 'counter' */, transitionTime: number, onTransitionEnd?: (id: number) => void) => { export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fade' | 'slide-fade' | 'none'/* | 'counter' */, transitionTime: number, onTransitionEnd?: (id: number) => void, isHeavy = true) => {
let animationFunction: TransitionFunction = null; let animationFunction: TransitionFunction = null;
switch(type) { switch(type) {
@ -62,12 +62,12 @@ export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigatio
content.dataset.animation = type; content.dataset.animation = type;
return Transition(content, animationFunction, transitionTime, onTransitionEnd); return Transition(content, animationFunction, transitionTime, onTransitionEnd, isHeavy);
}; };
type TransitionFunction = (tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) => void | (() => void); type TransitionFunction = (tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) => void | (() => void);
const Transition = (content: HTMLElement, animationFunction: TransitionFunction, transitionTime: number, onTransitionEnd?: (id: number) => void) => { const Transition = (content: HTMLElement, animationFunction: TransitionFunction, transitionTime: number, onTransitionEnd?: (id: number) => void, isHeavy = true) => {
const onTransitionEndCallbacks: Map<HTMLElement, Function> = new Map(); const onTransitionEndCallbacks: Map<HTMLElement, Function> = new Map();
let animationDeferred: CancellablePromise<void>; let animationDeferred: CancellablePromise<void>;
let animationStarted = 0; let animationStarted = 0;
@ -84,10 +84,16 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
const callback = onTransitionEndCallbacks.get(e.target as HTMLElement); const callback = onTransitionEndCallbacks.get(e.target as HTMLElement);
if(callback) callback(); if(callback) callback();
if(!animationDeferred || e.target !== from) return; if(e.target !== from) {
return;
}
animationDeferred.resolve(); if(!animationDeferred && isHeavy) return;
animationDeferred = undefined;
if(animationDeferred) {
animationDeferred.resolve();
animationDeferred = undefined;
}
if(onTransitionEnd) { if(onTransitionEnd) {
onTransitionEnd(selectTab.prevId); onTransitionEnd(selectTab.prevId);
@ -180,12 +186,14 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
}); });
} }
if(!animationDeferred) { if(isHeavy) {
animationDeferred = deferredPromise<void>(); if(!animationDeferred) {
animationStarted = performance.now(); animationDeferred = deferredPromise<void>();
animationStarted = performance.now();
}
dispatchHeavyAnimationEvent(animationDeferred, transitionTime * 2);
} }
dispatchHeavyAnimationEvent(animationDeferred, transitionTime * 2);
} }
self.prevId = id; self.prevId = id;

4
src/helpers/blur.ts

@ -1,10 +1,12 @@
import DEBUG from '../config/debug'; import _DEBUG from '../config/debug';
import fastBlur from '../vendor/fastBlur'; import fastBlur from '../vendor/fastBlur';
import pushHeavyTask from './heavyQueue'; import pushHeavyTask from './heavyQueue';
const RADIUS = 2; const RADIUS = 2;
const ITERATIONS = 2; const ITERATIONS = 2;
const DEBUG = _DEBUG && false;
function processBlur(dataUri: string, radius: number, iterations: number) { function processBlur(dataUri: string, radius: number, iterations: number) {
return new Promise<string>((resolve) => { return new Promise<string>((resolve) => {
const img = new Image(); const img = new Image();

2
src/helpers/fastSmoothScroll.ts

@ -43,7 +43,7 @@ export default function fastSmoothScroll(
return Promise.resolve(); */ return Promise.resolve(); */
} }
if(axis === 'y' && isInDOM(element) && container.getBoundingClientRect) { if(axis === 'y' && element !== container && isInDOM(element) && container.getBoundingClientRect) {
const elementRect = element.getBoundingClientRect(); const elementRect = element.getBoundingClientRect();
const containerRect = container.getBoundingClientRect(); const containerRect = container.getBoundingClientRect();

22
src/index.hbs

@ -98,17 +98,18 @@
<div class="sidebar-slider-item item-main"> <div class="sidebar-slider-item item-main">
<div class="sidebar-header"> <div class="sidebar-header">
<div class="sidebar-header__btn-container"> <div class="sidebar-header__btn-container">
<div class="btn-icon tgico-menu btn-menu-toggle rp sidebar-tools-button active"> <div class="animated-menu-icon"></div>
<div class="btn-icon btn-menu-toggle rp sidebar-tools-button active">
<div class="btn-menu bottom-right"> <div class="btn-menu bottom-right">
<div class="btn-menu-item menu-newGroup tgico-newgroup rp">New Group</div> <div class="btn-menu-item menu-newGroup tgico-newgroup rp">New Group</div>
<div class="btn-menu-item menu-contacts tgico-user rp">Contacts</div> <div class="btn-menu-item menu-contacts tgico-user rp">Contacts</div>
<div class="btn-menu-item menu-archived tgico-archive rp">Archived <span class="archived-count"></span></div> <div class="btn-menu-item menu-archived tgico-archive rp">Archived <span class="archived-count badge badge-24 badge-gray"></span></div>
<div class="btn-menu-item menu-saved tgico-savedmessages rp">Saved</div> <div class="btn-menu-item menu-saved tgico-savedmessages rp">Saved</div>
<div class="btn-menu-item menu-settings tgico-settings rp">Settings</div> <div class="btn-menu-item menu-settings tgico-settings rp">Settings</div>
<div class="btn-menu-item menu-help tgico-help rp btn-disabled">Help</div> <div class="btn-menu-item menu-help tgico-help rp btn-disabled">Help</div>
</div> </div>
</div> </div>
<div class="btn-icon tgico-arrow_back rp sidebar-back-button"></div> <div class="btn-icon rp sidebar-back-button"></div>
</div> </div>
</div> </div>
<div class="sidebar-content transition zoom-fade"> <div class="sidebar-content transition zoom-fade">
@ -171,9 +172,18 @@
<div class="sidebar-content sidebar-slider tabs-container"> <div class="sidebar-content sidebar-slider tabs-container">
<div class="sidebar-slider-item profile-container" id="shared-media-container"> <div class="sidebar-slider-item profile-container" id="shared-media-container">
<div class="sidebar-header"> <div class="sidebar-header">
<button class="btn-icon tgico sidebar-close-button"></button> <button class="btn-icon">
<div class="sidebar-header__title">Info</div> <div class="animated-close-icon"></div>
<div class="btn-icon tgico-more"></div> </button>
<div class="transition slide-fade">
<div class="transition-item">
<div class="sidebar-header__title">Info</div>
<div class="btn-icon tgico-more"></div>
</div>
<div class="transition-item">
<div class="sidebar-header__title">Shared Media</div>
</div>
</div>
</div> </div>
<div class="profile-content"> <div class="profile-content">
<div class="profile-content-wrapper"> <div class="profile-content-wrapper">

8
src/lib/appManagers/appImManager.ts

@ -518,7 +518,13 @@ export class AppImManager {
if(prevTabId !== -1 && prevTabId !== id && rootScope.settings.animationsEnabled) { if(prevTabId !== -1 && prevTabId !== id && rootScope.settings.animationsEnabled) {
const transitionTime = (mediaSizes.isMobile ? 250 : 200) + 100; // * cause transition time could be > 250ms const transitionTime = (mediaSizes.isMobile ? 250 : 200) + 100; // * cause transition time could be > 250ms
dispatchHeavyAnimationEvent(pause(transitionTime), transitionTime); const promise = pause(transitionTime);
dispatchHeavyAnimationEvent(promise, transitionTime);
this.columnEl.classList.add('disable-hover');
promise.finally(() => {
this.columnEl.classList.remove('disable-hover');
});
} }
this.tabId = id; this.tabId = id;

38
src/lib/mtproto/networker.ts

@ -1,5 +1,5 @@
import {isObject} from './bin_utils'; import {isObject} from './bin_utils';
import { bigStringInt} from './bin_utils'; import {bigStringInt} from './bin_utils';
import {TLDeserialization, TLSerialization} from './tl_utils'; import {TLDeserialization, TLSerialization} from './tl_utils';
import CryptoWorker from '../crypto/cryptoworker'; import CryptoWorker from '../crypto/cryptoworker';
import sessionStorage from '../sessionStorage'; import sessionStorage from '../sessionStorage';
@ -11,19 +11,13 @@ import { InvokeApiOptions } from '../../types';
import { longToBytes } from '../crypto/crypto_utils'; import { longToBytes } from '../crypto/crypto_utils';
import MTTransport from './transports/transport'; import MTTransport from './transports/transport';
import { convertToUint8Array, bufferConcat, bytesCmp, bytesToHex } from '../../helpers/bytes'; import { convertToUint8Array, bufferConcat, bytesCmp, bytesToHex } from '../../helpers/bytes';
import { nextRandomInt, randomLong } from '../../helpers/random'; import { nextRandomInt } from '../../helpers/random';
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise'; import { CancellablePromise } from '../../helpers/cancellablePromise';
import { isSafari } from '../../helpers/userAgent';
import App from '../../config/app'; import App from '../../config/app';
import DEBUG from '../../config/debug'; import DEBUG from '../../config/debug';
import Modes from '../../config/modes'; import Modes from '../../config/modes';
import Obfuscation from './transports/obfuscation';
/// #if MTPROTO_HTTP_UPLOAD /// #if MTPROTO_HTTP_UPLOAD || MTPROTO_HTTP
// @ts-ignore
import HTTP from './transports/http';
/// #elif MTPROTO_HTTP
// @ts-ignore
import HTTP from './transports/http'; import HTTP from './transports/http';
/// #endif /// #endif
@ -120,8 +114,6 @@ export default class MTPNetworker {
private debugRequests: Array<{before: Uint8Array, after: Uint8Array}> = []; private debugRequests: Array<{before: Uint8Array, after: Uint8Array}> = [];
private obfuscation: Obfuscation;
constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array, constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array,
serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) { serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) {
this.authKeyUint8 = convertToUint8Array(this.authKey); this.authKeyUint8 = convertToUint8Array(this.authKey);
@ -715,7 +707,7 @@ export default class MTPNetworker {
// * correct, fully checked // * correct, fully checked
public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) { public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) {
const x = isOut ? 0 : 8 const x = isOut ? 0 : 8;
const msgKeyLargePlain = bufferConcat(this.authKeyUint8.subarray(88 + x, 88 + x + 32), dataWithPadding); const msgKeyLargePlain = bufferConcat(this.authKeyUint8.subarray(88 + x, 88 + x + 32), dataWithPadding);
const msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain); const msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain);
@ -980,13 +972,13 @@ export default class MTPNetworker {
data.storeInt(message.body.length, 'message_data_length'); data.storeInt(message.body.length, 'message_data_length');
data.storeRawBytes(message.body, 'message_data'); data.storeRawBytes(message.body, 'message_data');
const des = new TLDeserialization(data.getBuffer().slice(16)); /* const des = new TLDeserialization(data.getBuffer().slice(16));
const desSalt = des.fetchLong(); const desSalt = des.fetchLong();
const desSessionId = des.fetchLong(); const desSessionId = des.fetchLong();
if(!this.isOnline) { if(!this.isOnline) {
this.log.error('trying to send message when offline', message, new Uint8Array(des.buffer), desSalt, desSessionId); this.log.error('trying to send message when offline', message, new Uint8Array(des.buffer), desSalt, desSessionId);
} } */
/* const messageDataLength = message.body.length; /* const messageDataLength = message.body.length;
let canBeLength = 0; // bytes let canBeLength = 0; // bytes
@ -1004,14 +996,14 @@ export default class MTPNetworker {
} */ } */
const paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5)); const paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5));
const padding = (message as any).padding || new Uint8Array(paddingLength).randomize()/* .fill(0) */; const padding = /* (message as any).padding || */new Uint8Array(paddingLength).randomize()/* .fill(0) */;
/* const padding = [167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157, /* const padding = [167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157,
51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150, 51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150,
94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28, 167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157, 94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28, 167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157,
51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150, 51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150,
94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28].slice(0, paddingLength); */ 94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28].slice(0, paddingLength); */
(message as any).padding = padding; //(message as any).padding = padding;
const dataWithPadding = bufferConcat(dataBuffer, padding); const dataWithPadding = bufferConcat(dataBuffer, padding);
// this.log('Adding padding', dataBuffer, padding, dataWithPadding) // this.log('Adding padding', dataBuffer, padding, dataWithPadding)
@ -1040,12 +1032,12 @@ export default class MTPNetworker {
const requestData = request.getBytes(true); const requestData = request.getBytes(true);
if(this.isFileNetworker) { // if(this.isFileNetworker) {
//this.log('Send encrypted: requestData length:', requestData.length, requestData.length % 16, paddingLength % 16, paddingLength, data.offset, encryptedResult.msgKey.length % 16, encryptedResult.bytes.length % 16); // //this.log('Send encrypted: requestData length:', requestData.length, requestData.length % 16, paddingLength % 16, paddingLength, data.offset, encryptedResult.msgKey.length % 16, encryptedResult.bytes.length % 16);
//this.log('Send encrypted: messageId:', message.msg_id, requestData.length); // //this.log('Send encrypted: messageId:', message.msg_id, requestData.length);
//this.log('Send encrypted:', message, new Uint8Array(bufferConcat(des.buffer, padding)), requestData, this.serverSalt.hex, this.sessionId.hex/* new Uint8Array(des.buffer) */); // //this.log('Send encrypted:', message, new Uint8Array(bufferConcat(des.buffer, padding)), requestData, this.serverSalt.hex, this.sessionId.hex/* new Uint8Array(des.buffer) */);
this.debugRequests.push({before: new Uint8Array(bufferConcat(des.buffer, padding)), after: requestData}); // this.debugRequests.push({before: new Uint8Array(bufferConcat(des.buffer, padding)), after: requestData});
} // }
return requestData; return requestData;
}); });

81
src/scss/partials/_animatedIcon.scss

@ -0,0 +1,81 @@
.animated-close-icon {
position: absolute;
transform: rotate(-45deg);
pointer-events: none;
&, &:before, &:after {
width: 1.125rem;
height: .125rem;
border-radius: .125rem;
background-color: var(--color-text-secondary);
transition: transform var(--slide-header-transition);
}
&:before, &:after {
position: absolute;
left: 0;
top: 0;
content: "";
}
&:before {
transform: rotate(90deg);
}
&.no-transition {
&, &:before, &:after {
transition: none;
}
}
&.state-back {
transform: rotate(180deg);
&:before {
transform: rotate(45deg) scaleX(.75) translateY(-.375rem);
}
&:after {
transform: rotate(-45deg) scaleX(.75) translateY(.375rem);
}
}
}
.animated-menu-icon {
position: absolute;
&, &:before, &:after {
width: 1.125rem;
height: .125rem;
border-radius: .125rem;
background-color: var(--color-text-secondary);
transition: transform .25s;
transform: rotate(0);
}
&:before, &:after {
position: absolute;
left: 0;
content: "";
}
&:before {
top: -.3125rem;
}
&:after {
top: .3125rem;
}
&.state-back {
transform: rotate(180deg);
&:before {
transform: rotate(45deg) scaleX(.75) translate(.375rem, -.1875rem);
}
&:after {
transform: rotate(-45deg) scaleX(.75) translate(.375rem, .1875rem);
}
}
}

15
src/scss/partials/_chat.scss

@ -84,11 +84,9 @@ $chat-helper-size: 39px;
--padding-horizontal: #{$chat-padding}; --padding-horizontal: #{$chat-padding};
} }
@include respond-to(esg-bottom) { .btn-circle {
.btn-circle { width: var(--chat-input-size);
height: $chat-input-handhelds-size; height: var(--chat-input-size);
width: $chat-input-handhelds-size;
}
} }
} }
@ -376,6 +374,10 @@ $chat-helper-size: 39px;
transition: .2s transform; transition: .2s transform;
} }
} }
.new-message-wrapper {
pointer-events: none;
}
} }
.bubbles.is-selecting:not(.backwards) ~ & { .bubbles.is-selecting:not(.backwards) ~ & {
@ -623,7 +625,7 @@ $chat-helper-size: 39px;
border-radius: 12px; border-radius: 12px;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
//box-shadow: 0 1px 2px 0 rgba(16, 35, 47, .07); //box-shadow: 0 1px 2px 0 rgba(16, 35, 47, .07);
box-shadow: 0px 1px 8px 1px rgba(0, 0, 0, 0.18); box-shadow: 0px 1px 8px 1px rgba(0, 0, 0, .18);
min-height: var(--chat-input-size); min-height: var(--chat-input-size);
max-height: 30rem; max-height: 30rem;
flex: 0 0 auto; flex: 0 0 auto;
@ -728,7 +730,6 @@ $chat-helper-size: 39px;
@include respond-to(esg-bottom) { @include respond-to(esg-bottom) {
--padding-vertical: .5px; --padding-vertical: .5px;
--padding-horizontal: .5rem; --padding-horizontal: .5rem;
min-height: $chat-input-handhelds-size;
} }
&:after { &:after {

39
src/scss/partials/_leftSidebar.scss

@ -57,6 +57,7 @@
border-bottom: 1px solid #dadce0; border-bottom: 1px solid #dadce0;
position: relative; position: relative;
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .16); box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .16);
top: unset;
.scrollable { .scrollable {
position: relative; position: relative;
@ -198,47 +199,39 @@
.sidebar-header__btn-container { .sidebar-header__btn-container {
position: relative; position: relative;
width: 40px; width: 2.5rem;
height: 40px; height: 2.5rem;
flex: 0 0 auto; flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
> .btn-icon { > .btn-icon {
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
position: absolute; position: absolute;
left: 0;
top: 0; top: 0;
right: 0;
bottom: 0;
left: 0;
transition: .2s opacity, .15s background-color; transition: .2s opacity, .15s background-color;
z-index: 2; z-index: 2;
margin: 0;
&:before {
transition: .2s transform;
transform: rotate(180deg);
}
body.animation-level-0 &, body.animation-level-0 &:before { body.animation-level-0 &, body.animation-level-0 &:before {
transition: none; transition: none;
} }
& + * {
margin-left: 0!important;
}
&.active { &.active {
//margin-top: 1px; //margin-top: 1px;
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
color: #707579;
&:before {
transform: rotate(0deg);
}
} }
} }
.btn-menu { .btn-menu {
@include respond-to(handhelds) { @include respond-to(handhelds) {
margin-top: -4px; margin-top: -.25rem;
} }
} }
} }
@ -247,20 +240,10 @@
min-width: 217px; min-width: 217px;
.archived-count { .archived-count {
border-radius: 12px;
min-width: 24px;
padding: 0 8px;
height: 24px;
text-align: center;
line-height: 24px;
color: #fff;
font-weight: 500;
background-color: #c5c9cc;
justify-self: flex-end; justify-self: flex-end;
margin-left: .625rem; margin-left: .625rem;
@include respond-to(handhelds) { @include respond-to(handhelds) {
font-size: 14px;
font-weight: 600; font-weight: 600;
} }
} }

13
src/scss/partials/_rightSidebar.scss

@ -47,7 +47,7 @@
.sidebar-header { .sidebar-header {
flex: 0 0 auto; flex: 0 0 auto;
.sidebar-close-button:before { .sidebar-close-button.tgico:before {
content: $tgico-arrow_back; content: $tgico-arrow_back;
@include respond-to(not-handhelds) { @include respond-to(not-handhelds) {
@ -235,6 +235,15 @@
max-height: calc((var(--vh, 1vh) * 100) - 100% - 56px); max-height: calc((var(--vh, 1vh) * 100) - 100% - 56px);
} }
} }
.sidebar-header .transition {
flex-grow: 1;
.transition-item {
display: flex;
align-items: center;
}
}
} }
.search-super { .search-super {
@ -278,7 +287,7 @@
box-shadow: none !important; box-shadow: none !important;
position: -webkit-sticky !important; position: -webkit-sticky !important;
position: sticky !important; position: sticky !important;
top: 0; top: -1px;
z-index: 2; z-index: 2;
background-color: #fff; background-color: #fff;

100
src/scss/style.scss

@ -47,8 +47,10 @@ $chat-padding-handhelds: .5rem;
--color-gray-hover: #{hover-color($color-gray)}; --color-gray-hover: #{hover-color($color-gray)};
--color-blue-hover: #{hover-color($color-blue)}; --color-blue-hover: #{hover-color($color-blue)};
--color-red-hover: #{hover-color($color-red)}; --color-red-hover: #{hover-color($color-red)};
--color-text-secondary: #{$color-gray};
--pm-transition: .2s ease-in-out; --pm-transition: .2s ease-in-out;
--layer-transition: .2s ease-in-out; --layer-transition: .2s ease-in-out;
--slide-header-transition: .4s ease-in-out;
--tabs-transition: .25s ease-in-out; --tabs-transition: .25s ease-in-out;
//--layer-transition: .3s cubic-bezier(.33, 1, .68, 1); //--layer-transition: .3s cubic-bezier(.33, 1, .68, 1);
//--layer-transition: none; //--layer-transition: none;
@ -93,6 +95,10 @@ $chat-padding-handhelds: .5rem;
--chat-input-padding: #{$chat-padding}; --chat-input-padding: #{$chat-padding};
} }
@include respond-to(esg-bottom) {
--chat-input-size: #{$chat-input-handhelds-size};
}
@include respond-to(only-medium-screens) { @include respond-to(only-medium-screens) {
--right-column-width: 25vw; --right-column-width: 25vw;
} }
@ -101,6 +107,7 @@ $chat-padding-handhelds: .5rem;
@import "partials/ico"; @import "partials/ico";
@import "partials/input"; @import "partials/input";
@import "partials/button"; @import "partials/button";
@import "partials/animatedIcon";
@import "partials/badge"; @import "partials/badge";
@import "partials/checkbox"; @import "partials/checkbox";
@import "partials/chatlist"; @import "partials/chatlist";
@ -847,6 +854,9 @@ img.emoji {
} }
.transition { .transition {
--easeOutSine: cubic-bezier(.39, .575, .565, 1);
--easeInSine: cubic-bezier(.47, 0, .745, .715);
.transition-item { .transition-item {
position: absolute; position: absolute;
top: 0; top: 0;
@ -905,6 +915,57 @@ img.emoji {
} }
} }
} }
/*
* slide-fade
*/
&.slide-fade {
position: relative;
> .from {
transform-origin: left center;
transform: translateX(0);
opacity: 1;
}
> .to {
transform-origin: left center;
transform: translateX(1.5rem);
opacity: 0;
}
&.animating {
> .from {
animation: fade-out-opacity .4s ease-out, slide-fade-out-move .4s;
}
> .to {
animation: fade-in-opacity .4s var(--easeInSine), slide-fade-in-move .4s;
}
}
}
&.slide-fade.backwards {
> .from {
transform: translateX(0);
opacity: 1;
}
> .to {
transform: translateX(-1.5rem);
opacity: 0;
}
&.animating {
> .from {
animation: fade-in-backwards-opacity .4s ease-out, slide-fade-in-backwards-move .4s;
}
> .to {
animation: fade-out-backwards-opacity .4s var(--easeOutSine), slide-fade-out-backwards-move .4s;
}
}
}
} }
/* /*
@ -937,6 +998,45 @@ img.emoji {
} }
} }
/*
* slide-fade
*/
@keyframes slide-fade-in-move {
0% {
transform: translateX(1.5rem);
}
100% {
transform: translateX(0);
}
}
@keyframes slide-fade-out-move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-1.5rem);
}
}
@keyframes slide-fade-in-backwards-move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(1.5rem);
}
}
@keyframes slide-fade-out-backwards-move {
0% {
transform: translateX(-1.5rem);
}
100% {
transform: translateX(0);
}
}
/* .zoom-fade { /* .zoom-fade {
transition: .15s ease-in-out opacity, .15s ease-in-out transform; transition: .15s ease-in-out opacity, .15s ease-in-out transform;
transform: scale3d(1.1, 1.1, 1); transform: scale3d(1.1, 1.1, 1);

Loading…
Cancel
Save