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. 2
      src/components/horizontalMenu.ts
  3. 7
      src/components/scrollable.ts
  4. 2
      src/components/sidebarLeft/index.ts
  5. 2
      src/components/sidebarRight/index.ts
  6. 40
      src/components/sidebarRight/tabs/sharedMedia.ts
  7. 3
      src/components/slider.ts
  8. 16
      src/components/transition.ts
  9. 4
      src/helpers/blur.ts
  10. 2
      src/helpers/fastSmoothScroll.ts
  11. 18
      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. 13
      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 { @@ -80,7 +80,7 @@ export default class AppSearchSuper {
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.classList.add('search-super');
@ -549,7 +549,7 @@ export default class AppSearchSuper { @@ -549,7 +549,7 @@ export default class AppSearchSuper {
const method = append ? 'append' : 'prepend';
elemsToAppend.forEach(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.dataset.mid = '' + message.mid;
element.dataset.peerId = '' + message.peerId;

2
src/components/horizontalMenu.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { findUpTag, whichChild, findUpAsChild } from "../helpers/dom";
import { whichChild, findUpAsChild } from "../helpers/dom";
import { TransitionSlider } from "./transition";
import { ScrollableX } from "./scrollable";
import rootScope from "../lib/rootScope";

7
src/components/scrollable.ts

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

2
src/components/sidebarLeft/index.ts

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

2
src/components/sidebarRight/index.ts

@ -103,7 +103,7 @@ export class AppSidebarRight extends SidebarSlider { @@ -103,7 +103,7 @@ export class AppSidebarRight extends SidebarSlider {
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);
if(transitionTime) {
dispatchHeavyAnimationEvent(promise, transitionTime);

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

@ -11,6 +11,9 @@ import AvatarElement from "../../avatar"; @@ -11,6 +11,9 @@ import AvatarElement from "../../avatar";
import Scrollable from "../../scrollable";
import { SliderTab } from "../../slider";
import CheckboxField from "../../checkbox";
import { attachClickEvent, cancelEvent } from "../../../helpers/dom";
import appSidebarRight from "..";
import { TransitionSlider } from "../../transition";
let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => {
@ -29,7 +32,7 @@ let setText = (text: string, el: HTMLDivElement) => { @@ -29,7 +32,7 @@ let setText = (text: string, el: HTMLDivElement) => {
// TODO: отредактированное сообщение не изменится
export default class AppSharedMediaTab implements SliderTab {
public container: HTMLElement;
public closeBtn: HTMLElement;
public closeBtn: HTMLButtonElement;
private peerId = 0;
private threadId = 0;
@ -62,7 +65,8 @@ export default class AppSharedMediaTab implements SliderTab { @@ -62,7 +65,8 @@ export default class AppSharedMediaTab implements SliderTab {
public init() {
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.profileElements = {
@ -87,6 +91,36 @@ export default class AppSharedMediaTab implements SliderTab { @@ -87,6 +91,36 @@ export default class AppSharedMediaTab implements SliderTab {
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', () => {
//let checked = this.profileElements.notificationsCheckbox.checked;
appMessagesManager.mutePeer(this.peerId);
@ -134,7 +168,7 @@ export default class AppSharedMediaTab implements SliderTab { @@ -134,7 +168,7 @@ export default class AppSharedMediaTab implements SliderTab {
}, {
inputFilter: 'inputMessagesFilterMusic',
name: 'Music'
}], this.scroll);
}], this.scroll/* , undefined, undefined, false */);
this.profileContentEl.append(this.searchSuper.container);
}

3
src/components/slider.ts

@ -2,6 +2,7 @@ import { attachClickEvent } from "../helpers/dom"; @@ -2,6 +2,7 @@ import { attachClickEvent } from "../helpers/dom";
import { horizontalMenu } from "./horizontalMenu";
import ButtonIcon from "./buttonIcon";
import Scrollable from "./scrollable";
import { TransitionSlider } from "./transition";
export interface SliderTab {
onOpen?: () => void,
@ -86,7 +87,7 @@ export default class SidebarSlider { @@ -86,7 +87,7 @@ export default class SidebarSlider {
constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab} = {}, private canHideFirst = false) {
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) {
this._selectTab(0);
}

16
src/components/transition.ts

@ -46,7 +46,7 @@ function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight @@ -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;
switch(type) {
@ -62,12 +62,12 @@ export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigatio @@ -62,12 +62,12 @@ export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigatio
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);
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();
let animationDeferred: CancellablePromise<void>;
let animationStarted = 0;
@ -84,10 +84,16 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, @@ -84,10 +84,16 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
const callback = onTransitionEndCallbacks.get(e.target as HTMLElement);
if(callback) callback();
if(!animationDeferred || e.target !== from) return;
if(e.target !== from) {
return;
}
if(!animationDeferred && isHeavy) return;
if(animationDeferred) {
animationDeferred.resolve();
animationDeferred = undefined;
}
if(onTransitionEnd) {
onTransitionEnd(selectTab.prevId);
@ -180,6 +186,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, @@ -180,6 +186,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
});
}
if(isHeavy) {
if(!animationDeferred) {
animationDeferred = deferredPromise<void>();
animationStarted = performance.now();
@ -187,6 +194,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, @@ -187,6 +194,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
dispatchHeavyAnimationEvent(animationDeferred, transitionTime * 2);
}
}
self.prevId = id;
from = to;

4
src/helpers/blur.ts

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

2
src/helpers/fastSmoothScroll.ts

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

18
src/index.hbs

@ -98,17 +98,18 @@ @@ -98,17 +98,18 @@
<div class="sidebar-slider-item item-main">
<div class="sidebar-header">
<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-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-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-settings tgico-settings rp">Settings</div>
<div class="btn-menu-item menu-help tgico-help rp btn-disabled">Help</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 class="sidebar-content transition zoom-fade">
@ -171,10 +172,19 @@ @@ -171,10 +172,19 @@
<div class="sidebar-content sidebar-slider tabs-container">
<div class="sidebar-slider-item profile-container" id="shared-media-container">
<div class="sidebar-header">
<button class="btn-icon tgico sidebar-close-button"></button>
<button class="btn-icon">
<div class="animated-close-icon"></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 class="profile-content">
<div class="profile-content-wrapper">
<avatar-element class="profile-avatar avatar-120" dialog="1" clickable></avatar-element>

8
src/lib/appManagers/appImManager.ts

@ -518,7 +518,13 @@ export class AppImManager { @@ -518,7 +518,13 @@ export class AppImManager {
if(prevTabId !== -1 && prevTabId !== id && rootScope.settings.animationsEnabled) {
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;

38
src/lib/mtproto/networker.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import {isObject} from './bin_utils';
import { bigStringInt} from './bin_utils';
import {bigStringInt} from './bin_utils';
import {TLDeserialization, TLSerialization} from './tl_utils';
import CryptoWorker from '../crypto/cryptoworker';
import sessionStorage from '../sessionStorage';
@ -11,19 +11,13 @@ import { InvokeApiOptions } from '../../types'; @@ -11,19 +11,13 @@ import { InvokeApiOptions } from '../../types';
import { longToBytes } from '../crypto/crypto_utils';
import MTTransport from './transports/transport';
import { convertToUint8Array, bufferConcat, bytesCmp, bytesToHex } from '../../helpers/bytes';
import { nextRandomInt, randomLong } from '../../helpers/random';
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
import { isSafari } from '../../helpers/userAgent';
import { nextRandomInt } from '../../helpers/random';
import { CancellablePromise } from '../../helpers/cancellablePromise';
import App from '../../config/app';
import DEBUG from '../../config/debug';
import Modes from '../../config/modes';
import Obfuscation from './transports/obfuscation';
/// #if MTPROTO_HTTP_UPLOAD
// @ts-ignore
import HTTP from './transports/http';
/// #elif MTPROTO_HTTP
// @ts-ignore
/// #if MTPROTO_HTTP_UPLOAD || MTPROTO_HTTP
import HTTP from './transports/http';
/// #endif
@ -120,8 +114,6 @@ export default class MTPNetworker { @@ -120,8 +114,6 @@ export default class MTPNetworker {
private debugRequests: Array<{before: Uint8Array, after: Uint8Array}> = [];
private obfuscation: Obfuscation;
constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array,
serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) {
this.authKeyUint8 = convertToUint8Array(this.authKey);
@ -715,7 +707,7 @@ export default class MTPNetworker { @@ -715,7 +707,7 @@ export default class MTPNetworker {
// * correct, fully checked
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 msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain);
@ -980,13 +972,13 @@ export default class MTPNetworker { @@ -980,13 +972,13 @@ export default class MTPNetworker {
data.storeInt(message.body.length, 'message_data_length');
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 desSessionId = des.fetchLong();
if(!this.isOnline) {
this.log.error('trying to send message when offline', message, new Uint8Array(des.buffer), desSalt, desSessionId);
}
} */
/* const messageDataLength = message.body.length;
let canBeLength = 0; // bytes
@ -1004,14 +996,14 @@ export default class MTPNetworker { @@ -1004,14 +996,14 @@ export default class MTPNetworker {
} */
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,
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,
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); */
(message as any).padding = padding;
//(message as any).padding = padding;
const dataWithPadding = bufferConcat(dataBuffer, padding);
// this.log('Adding padding', dataBuffer, padding, dataWithPadding)
@ -1040,12 +1032,12 @@ export default class MTPNetworker { @@ -1040,12 +1032,12 @@ export default class MTPNetworker {
const requestData = request.getBytes(true);
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: 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.debugRequests.push({before: new Uint8Array(bufferConcat(des.buffer, padding)), after: requestData});
}
// 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: 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.debugRequests.push({before: new Uint8Array(bufferConcat(des.buffer, padding)), after: requestData});
// }
return requestData;
});

81
src/scss/partials/_animatedIcon.scss

@ -0,0 +1,81 @@ @@ -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);
}
}
}

13
src/scss/partials/_chat.scss

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

39
src/scss/partials/_leftSidebar.scss

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

13
src/scss/partials/_rightSidebar.scss

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

100
src/scss/style.scss

@ -47,8 +47,10 @@ $chat-padding-handhelds: .5rem; @@ -47,8 +47,10 @@ $chat-padding-handhelds: .5rem;
--color-gray-hover: #{hover-color($color-gray)};
--color-blue-hover: #{hover-color($color-blue)};
--color-red-hover: #{hover-color($color-red)};
--color-text-secondary: #{$color-gray};
--pm-transition: .2s ease-in-out;
--layer-transition: .2s ease-in-out;
--slide-header-transition: .4s ease-in-out;
--tabs-transition: .25s ease-in-out;
//--layer-transition: .3s cubic-bezier(.33, 1, .68, 1);
//--layer-transition: none;
@ -93,6 +95,10 @@ $chat-padding-handhelds: .5rem; @@ -93,6 +95,10 @@ $chat-padding-handhelds: .5rem;
--chat-input-padding: #{$chat-padding};
}
@include respond-to(esg-bottom) {
--chat-input-size: #{$chat-input-handhelds-size};
}
@include respond-to(only-medium-screens) {
--right-column-width: 25vw;
}
@ -101,6 +107,7 @@ $chat-padding-handhelds: .5rem; @@ -101,6 +107,7 @@ $chat-padding-handhelds: .5rem;
@import "partials/ico";
@import "partials/input";
@import "partials/button";
@import "partials/animatedIcon";
@import "partials/badge";
@import "partials/checkbox";
@import "partials/chatlist";
@ -847,6 +854,9 @@ img.emoji { @@ -847,6 +854,9 @@ img.emoji {
}
.transition {
--easeOutSine: cubic-bezier(.39, .575, .565, 1);
--easeInSine: cubic-bezier(.47, 0, .745, .715);
.transition-item {
position: absolute;
top: 0;
@ -905,6 +915,57 @@ img.emoji { @@ -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 { @@ -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 {
transition: .15s ease-in-out opacity, .15s ease-in-out transform;
transform: scale3d(1.1, 1.1, 1);

Loading…
Cancel
Save