Browse Source

Sticker fixes

Some layout fixes
master
morethanwords 4 years ago
parent
commit
3e978a8244
  1. 34
      src/components/animationIntersector.ts
  2. 15
      src/components/emoticonsDropdown/tabs/stickers.ts
  3. 15
      src/components/horizontalMenu.ts
  4. 8
      src/components/sidebarRight/sharedMedia.ts
  5. 36
      src/helpers/mediaSizes.ts
  6. 2
      src/lib/appManagers/appDialogsManager.ts
  7. 81
      src/lib/appManagers/appImManager.ts
  8. 5
      src/lib/appManagers/appMessagesManager.ts
  9. 22
      src/lib/appManagers/appSidebarRight.ts
  10. 18
      src/lib/appManagers/appStateManager.ts
  11. 25
      src/lib/appManagers/appStickersManager.ts
  12. 67
      src/lib/lottieLoader.ts
  13. 17
      src/lib/rlottie/rlottie.worker.ts
  14. 67
      src/scss/partials/_chat.scss
  15. 10
      src/scss/partials/_chatBubble.scss
  16. 242
      src/scss/partials/_emojiDropdown.scss
  17. 35
      src/scss/partials/_leftSidebar.scss
  18. 22
      src/scss/partials/_rightSidebar.scss
  19. 2
      src/scss/partials/_ripple.scss
  20. 1
      src/scss/partials/_sidebar.scss
  21. 20
      src/scss/partials/pages/_chats.scss
  22. 55
      src/scss/style.scss

34
src/components/animationIntersector.ts

@ -63,6 +63,24 @@ export class AnimationIntersector {
return found; return found;
} }
public removeAnimation(player: AnimationItem) {
//console.log('destroy animation');
const {el, animation} = player;
animation.remove();
if(animation instanceof HTMLVideoElement) {
animation.src = '';
animation.load();
}
for(const group in this.byGroups) {
this.byGroups[group].findAndSplice(p => p == player);
}
this.observer.unobserve(el);
this.visible.delete(player);
}
public addAnimation(animation: RLottiePlayer | HTMLVideoElement, group = '') { public addAnimation(animation: RLottiePlayer | HTMLVideoElement, group = '') {
const player = { const player = {
el: animation instanceof RLottiePlayer ? animation.el : animation, el: animation instanceof RLottiePlayer ? animation.el : animation,
@ -98,20 +116,7 @@ export class AnimationIntersector {
const {el, animation, group} = player; const {el, animation, group} = player;
//return; //return;
if((destroy || (!isInDOM(el) && !this.lockedGroups[group]))/* && false */) { if((destroy || (!isInDOM(el) && !this.lockedGroups[group]))/* && false */) {
//console.log('destroy animation'); this.removeAnimation(player);
animation.remove();
if(animation instanceof HTMLVideoElement) {
animation.src = '';
animation.load();
}
for(const group in this.byGroups) {
this.byGroups[group].findAndSplice(p => p == player);
}
this.observer.unobserve(el);
this.visible.delete(player);
return; return;
} }
@ -161,7 +166,6 @@ export class AnimationIntersector {
} }
const animationIntersector = new AnimationIntersector(); const animationIntersector = new AnimationIntersector();
// @ts-ignore
if(process.env.NODE_ENV == 'development') { if(process.env.NODE_ENV == 'development') {
(window as any).animationIntersector = animationIntersector; (window as any).animationIntersector = animationIntersector;
} }

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

@ -13,7 +13,8 @@ import apiManager from "../../../lib/mtproto/mtprotoworker";
import StickyIntersector from "../../stickyIntersector"; import StickyIntersector from "../../stickyIntersector";
import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager"; import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager";
import animationIntersector from "../../animationIntersector"; import animationIntersector from "../../animationIntersector";
import LazyLoadQueue, { LazyLoadQueueRepeat } from "../../lazyLoadQueue"; import { LazyLoadQueueRepeat } from "../../lazyLoadQueue";
import mediaSizes from "../../../helpers/mediaSizes";
export default class StickersTab implements EmoticonsTab { export default class StickersTab implements EmoticonsTab {
public content: HTMLElement; public content: HTMLElement;
@ -81,6 +82,7 @@ export default class StickersTab implements EmoticonsTab {
renderSticker(doc: MyDocument, div?: HTMLDivElement) { renderSticker(doc: MyDocument, div?: HTMLDivElement) {
if(!div) { if(!div) {
div = document.createElement('div'); div = document.createElement('div');
div.classList.add('grid-item');
if(doc.sticker == 2) { if(doc.sticker == 2) {
this.animatedDivs.add(div); this.animatedDivs.add(div);
@ -92,13 +94,10 @@ export default class StickersTab implements EmoticonsTab {
} }
} }
// * This will wrap only a thumb
wrapSticker({ wrapSticker({
doc, doc,
div, div,
/* width: 80,
height: 80,
play: false,
loop: false, */
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue, lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
group: EMOTICONSSTICKERGROUP, group: EMOTICONSSTICKERGROUP,
onlyThumb: doc.sticker == 2 onlyThumb: doc.sticker == 2
@ -183,12 +182,14 @@ export default class StickersTab implements EmoticonsTab {
processVisibleDiv = (div: HTMLElement) => { processVisibleDiv = (div: HTMLElement) => {
const docID = div.dataset.docID; const docID = div.dataset.docID;
const doc = appDocsManager.getDoc(docID); const doc = appDocsManager.getDoc(docID);
const size = mediaSizes.active.esgSticker.width;
const promise = wrapSticker({ const promise = wrapSticker({
doc, doc,
div: div as HTMLDivElement, div: div as HTMLDivElement,
width: 80, width: size,
height: 80, height: size,
lazyLoadQueue: null, lazyLoadQueue: null,
group: EMOTICONSSTICKERGROUP, group: EMOTICONSSTICKERGROUP,
onlyThumb: false, onlyThumb: false,

15
src/components/horizontalMenu.ts

@ -31,7 +31,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
let prevTabContent: HTMLElement = null; let prevTabContent: HTMLElement = null;
let prevId = -1; let prevId = -1;
const selectTab = (id: number) => { const selectTab = (id: number, animate = true) => {
if(id == prevId) return false; if(id == prevId) return false;
//console.log('selectTab id:', id); //console.log('selectTab id:', id);
@ -39,7 +39,8 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
const p = prevTabContent; const p = prevTabContent;
const tabContent = content.children[id] as HTMLElement; const tabContent = content.children[id] as HTMLElement;
if(content.dataset.slider == 'none') { // * means animation isn't needed
if(content.dataset.slider == 'none' || !animate) {
if(p) { if(p) {
p.classList.remove('active'); p.classList.remove('active');
} }
@ -53,6 +54,10 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
return; return;
} }
if(prevTabContent) {
prevTabContent.classList.remove('to');
}
const toRight = prevId < id; const toRight = prevId < id;
if(!tabContent) { if(!tabContent) {
//prevTabContent.classList.remove('active'); //prevTabContent.classList.remove('active');
@ -64,6 +69,8 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
} else { } else {
slideNavigation(tabContent, prevTabContent, width, toRight); slideNavigation(tabContent, prevTabContent, width, toRight);
} }
tabContent.classList.add('to');
} else { } else {
tabContent.classList.add('active'); tabContent.classList.add('active');
} }
@ -76,6 +83,10 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
p.style.filter = ''; p.style.filter = '';
p.classList.remove('active'); p.classList.remove('active');
if(tabContent) {
tabContent.classList.remove('to');
}
delete hideTimeouts[_prevId]; delete hideTimeouts[_prevId];
if(onTransitionEnd) onTransitionEnd(); if(onTransitionEnd) onTransitionEnd();

8
src/components/sidebarRight/sharedMedia.ts

@ -324,7 +324,7 @@ export default class AppSharedMediaTab implements SliderTab {
const media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document); const media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
const div = document.createElement('div'); const div = document.createElement('div');
div.classList.add('media-item'); div.classList.add('grid-item');
//console.log(message, photo); //console.log(message, photo);
const isPhoto = media._ == 'photo'; const isPhoto = media._ == 'photo';
@ -387,7 +387,7 @@ export default class AppSharedMediaTab implements SliderTab {
const willHaveThumb = !isDownloaded && sizes && sizes[0].bytes; const willHaveThumb = !isDownloaded && sizes && sizes[0].bytes;
if(willHaveThumb) { if(willHaveThumb) {
thumb = new Image(); thumb = new Image();
thumb.classList.add('media-image', 'thumbnail'); thumb.classList.add('grid-item-media', 'thumbnail');
thumb.dataset.mid = '' + message.mid; thumb.dataset.mid = '' + message.mid;
appPhotosManager.setAttachmentPreview(sizes[0].bytes, thumb, false, false); appPhotosManager.setAttachmentPreview(sizes[0].bytes, thumb, false, false);
div.append(thumb); div.append(thumb);
@ -396,7 +396,7 @@ export default class AppSharedMediaTab implements SliderTab {
const needBlur = !isDownloaded || !willHaveThumb; const needBlur = !isDownloaded || !willHaveThumb;
const img = new Image(); const img = new Image();
img.dataset.mid = '' + message.mid; img.dataset.mid = '' + message.mid;
img.classList.add('media-image'); img.classList.add('grid-item-media');
if(needBlur) img.style.opacity = '0'; if(needBlur) img.style.opacity = '0';
div.append(img); div.append(img);
@ -690,7 +690,7 @@ export default class AppSharedMediaTab implements SliderTab {
for(let i = 0; i < 1500; ++i) { for(let i = 0; i < 1500; ++i) {
let div = document.createElement('div'); let div = document.createElement('div');
div.insertAdjacentHTML('beforeend', `<img class="media-image" src="assets/img/camomile.jpg">`); div.insertAdjacentHTML('beforeend', `<img class="media-image" src="assets/img/camomile.jpg">`);
div.classList.add('media-item'); div.classList.add('grid-item');
div.dataset.id = '' + (i / 3 | 0); div.dataset.id = '' + (i / 3 | 0);
//div.innerText = '' + (i / 3 | 0); //div.innerText = '' + (i / 3 | 0);
this.sharedMedia.contentMedia.append(div); this.sharedMedia.contentMedia.append(div);

36
src/helpers/mediaSizes.ts

@ -1,10 +1,11 @@
import EventListenerBase from "./eventListenerBase"; import EventListenerBase from "./eventListenerBase";
type Size = {width: number, height: number}; type Size = Partial<{width: number, height: number}>;
type Sizes = { type Sizes = {
regular: Size, regular: Size,
webpage: Size, webpage: Size,
album: Size album: Size,
esgSticker: Size
}; };
export enum ScreenSize { export enum ScreenSize {
@ -13,7 +14,7 @@ export enum ScreenSize {
large large
} }
const MOBILE_SIZE = 896; const MOBILE_SIZE = 600;
const MEDIUM_SIZE = 1275; const MEDIUM_SIZE = 1275;
const LARGE_SIZE = 1680; const LARGE_SIZE = 1680;
@ -21,7 +22,7 @@ class MediaSizes extends EventListenerBase<{
changeScreen: (from: ScreenSize, to: ScreenSize) => void changeScreen: (from: ScreenSize, to: ScreenSize) => void
}> { }> {
private screenSizes: {key: ScreenSize, value: number}[] = [ private screenSizes: {key: ScreenSize, value: number}[] = [
{key: ScreenSize.mobile, value: MOBILE_SIZE - 1}, {key: ScreenSize.mobile, value: MOBILE_SIZE},
{key: ScreenSize.medium, value: MEDIUM_SIZE}, {key: ScreenSize.medium, value: MEDIUM_SIZE},
{key: ScreenSize.large, value: LARGE_SIZE} {key: ScreenSize.large, value: LARGE_SIZE}
]; ];
@ -39,7 +40,8 @@ class MediaSizes extends EventListenerBase<{
album: { album: {
width: 293, width: 293,
height: 0 height: 0
} },
esgSticker: {}
}, },
desktop: { desktop: {
regular: { regular: {
@ -53,7 +55,8 @@ class MediaSizes extends EventListenerBase<{
album: { album: {
width: 451, width: 451,
height: 0 height: 0
} },
esgSticker: {}
} }
}; };
@ -80,17 +83,24 @@ class MediaSizes extends EventListenerBase<{
} }
} }
if(this.activeScreen != activeScreen && this.activeScreen !== undefined) { const wasScreen = this.activeScreen;
//console.log('changeScreen', this.activeScreen, activeScreen);
this.setListenerResult('changeScreen', this.activeScreen, activeScreen);
}
this.activeScreen = activeScreen; this.activeScreen = activeScreen;
this.isMobile = this.activeScreen == ScreenSize.mobile; this.isMobile = this.activeScreen == ScreenSize.mobile;
this.active = this.isMobile ? this.sizes.handhelds : this.sizes.desktop; this.active = this.isMobile ? this.sizes.handhelds : this.sizes.desktop;
//console.time('esg');
const computedStyle = window.getComputedStyle(document.documentElement);
this.active.esgSticker.width = parseFloat(computedStyle.getPropertyValue('--esg-sticker-size'));
//console.timeEnd('esg');
if(wasScreen != activeScreen) {
//console.log('changeScreen', this.activeScreen, activeScreen);
if(wasScreen !== undefined) {
this.setListenerResult('changeScreen', this.activeScreen, activeScreen);
}
}
/* if(this.isMobile) { /* if(this.isMobile) {
for(let i in this.active) { for(let i in this.active) {
// @ts-ignore // @ts-ignore

2
src/lib/appManagers/appDialogsManager.ts

@ -618,7 +618,7 @@ export class AppDialogsManager {
//selectTab(0); //selectTab(0);
(this.folders.menu.firstElementChild.firstElementChild as HTMLElement).click(); (this.folders.menu.firstElementChild.firstElementChild as HTMLElement).click();
/* false && */appStateManager.loadSavedState().then(() => { /* false && */appStateManager.getState().then(() => {
return appMessagesManager.filtersStorage.getDialogFilters(); return appMessagesManager.filtersStorage.getDialogFilters();
}).then(filters => { }).then(filters => {
for(const filterID in filters) { for(const filterID in filters) {

81
src/lib/appManagers/appImManager.ts

@ -8,7 +8,7 @@ import appProfileManager from "./appProfileManager";
import appDialogsManager from "./appDialogsManager"; import appDialogsManager from "./appDialogsManager";
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
import appPhotosManager from "./appPhotosManager"; import appPhotosManager from "./appPhotosManager";
import appSidebarRight, { AppSidebarRight } from './appSidebarRight'; import appSidebarRight, { AppSidebarRight, RIGHT_COLUMN_ACTIVE_CLASSNAME } from './appSidebarRight';
import { logger, LogLevels } from "../logger"; import { logger, LogLevels } from "../logger";
import appMediaViewer from "./appMediaViewer"; import appMediaViewer from "./appMediaViewer";
@ -46,9 +46,11 @@ import { isAndroid, isApple, isSafari } from '../../helpers/userAgent';
appSidebarLeft; // just to include appSidebarLeft; // just to include
const testScroll = false; const TEST_SCROLL = false;
const ANIMATIONGROUP = 'chat'; const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
const ANIMATION_GROUP = 'chat';
export class AppImManager { export class AppImManager {
public columnEl = document.getElementById('column-center') as HTMLDivElement; public columnEl = document.getElementById('column-center') as HTMLDivElement;
@ -138,14 +140,20 @@ export class AppImManager {
private cleanupID = 0; private cleanupID = 0;
public selectTab = horizontalMenu(null, document.getElementById('main-columns')); private mainColumns: HTMLElement;
public _selectTab: ReturnType<typeof horizontalMenu>;
private closeBtn = this.topbar.querySelector('.sidebar-close-button') as HTMLButtonElement; private closeBtn = this.topbar.querySelector('.sidebar-close-button') as HTMLButtonElement;
public hideRightSidebar = false;
constructor() { constructor() {
this.log = logger('IM', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error); this.log = logger('IM', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error);
this.chatInputC = new ChatInput(); this.chatInputC = new ChatInput();
this.preloader = new ProgressivePreloader(null, false); this.preloader = new ProgressivePreloader(null, false);
this.mainColumns = document.getElementById('main-columns');
this._selectTab = horizontalMenu(null, this.mainColumns);
this.selectTab(0); this.selectTab(0);
parseMenuButtonsTo(this.menuButtons, this.columnEl.querySelector('.chat-more-button').firstElementChild.children); parseMenuButtonsTo(this.menuButtons, this.columnEl.querySelector('.chat-more-button').firstElementChild.children);
this.chatAudio = new ChatAudio(); this.chatAudio = new ChatAudio();
@ -553,7 +561,19 @@ export class AppImManager {
this.closeBtn.addEventListener('click', (e) => { this.closeBtn.addEventListener('click', (e) => {
cancelEvent(e); cancelEvent(e);
this.setPeer(0);
if(mediaSizes.isMobile) {
this.setPeer(0);
} else {
const isNowOpen = document.body.classList.toggle(LEFT_COLUMN_ACTIVE_CLASSNAME);
if(isNowOpen && document.body.classList.contains(RIGHT_COLUMN_ACTIVE_CLASSNAME)) {
appSidebarRight.toggleSidebar(false, false);
this.hideRightSidebar = isNowOpen;
} else if(this.hideRightSidebar) {
appSidebarRight.toggleSidebar(true);
}
}
}); });
this.searchBtn.addEventListener('click', (e) => { this.searchBtn.addEventListener('click', (e) => {
@ -702,6 +722,12 @@ export class AppImManager {
}); });
} }
public selectTab(id: number) {
document.body.classList.toggle(LEFT_COLUMN_ACTIVE_CLASSNAME, id == 0);
this._selectTab(id, mediaSizes.isMobile);
}
onDatePick = (timestamp: number) => { onDatePick = (timestamp: number) => {
const peerID = this.peerID; const peerID = this.peerID;
appMessagesManager.requestHistory(peerID, 0, 2, -1, timestamp).then(history => { appMessagesManager.requestHistory(peerID, 0, 2, -1, timestamp).then(history => {
@ -773,7 +799,7 @@ export class AppImManager {
public loadMoreHistory(top: boolean, justLoad = false) { public loadMoreHistory(top: boolean, justLoad = false) {
//this.log('loadMoreHistory', top); //this.log('loadMoreHistory', top);
if(!this.peerID || testScroll || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return; if(!this.peerID || TEST_SCROLL || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return;
// warning, если иды только отрицательные то вниз не попадёт (хотя мб и так не попадёт) // warning, если иды только отрицательные то вниз не попадёт (хотя мб и так не попадёт)
let history = Object.keys(this.bubbles).map(id => +id).filter(id => id > 0).sort((a, b) => a - b); let history = Object.keys(this.bubbles).map(id => +id).filter(id => id > 0).sort((a, b) => a - b);
@ -1000,6 +1026,10 @@ export class AppImManager {
return !!this.bubbles[id]?.parentElement; return !!this.bubbles[id]?.parentElement;
})]; })];
} }
/* public selectTab() {
} */
public setPeer(peerID: number, lastMsgID?: number) { public setPeer(peerID: number, lastMsgID?: number) {
//console.time('appImManager setPeer'); //console.time('appImManager setPeer');
@ -1012,11 +1042,18 @@ export class AppImManager {
this.cleanup(true); this.cleanup(true);
this.peerID = $rootScope.selectedPeerID = 0; this.peerID = $rootScope.selectedPeerID = 0;
$rootScope.$broadcast('peer_changed', this.peerID); $rootScope.$broadcast('peer_changed', this.peerID);
if(mediaSizes.isMobile) {
this.selectTab(0); this.selectTab(0);
}
document.body.classList.add(LEFT_COLUMN_ACTIVE_CLASSNAME);
return false; return false;
} }
document.body.classList.remove(LEFT_COLUMN_ACTIVE_CLASSNAME);
if(this.hideRightSidebar) {
appSidebarRight.toggleSidebar(true);
this.hideRightSidebar = false;
}
const samePeer = this.peerID == peerID; const samePeer = this.peerID == peerID;
@ -1107,14 +1144,12 @@ export class AppImManager {
this.preloader.attach(this.bubblesContainer); this.preloader.attach(this.bubblesContainer);
if(mediaSizes.isMobile) { this.selectTab(1);
this.selectTab(1);
}
} }
//console.timeEnd('appImManager setPeer pre promise'); //console.timeEnd('appImManager setPeer pre promise');
animationIntersector.lockGroup(ANIMATIONGROUP); animationIntersector.lockGroup(ANIMATION_GROUP);
this.setPeerPromise = Promise.all([ this.setPeerPromise = Promise.all([
promise.then(() => { promise.then(() => {
////this.log('setPeer removing preloader'); ////this.log('setPeer removing preloader');
@ -1127,16 +1162,14 @@ export class AppImManager {
this.finishPeerChange(); this.finishPeerChange();
} }
if(mediaSizes.isMobile) { this.selectTab(1);
this.selectTab(1);
}
} else { } else {
this.preloader.detach(); this.preloader.detach();
} }
this.scrollable.container.append(this.chatInner); this.scrollable.container.append(this.chatInner);
animationIntersector.unlockGroup(ANIMATIONGROUP); animationIntersector.unlockGroup(ANIMATION_GROUP);
animationIntersector.checkAnimations(false, ANIMATIONGROUP/* , true */); animationIntersector.checkAnimations(false, ANIMATION_GROUP/* , true */);
//this.scrollable.attachSentinels(); //this.scrollable.attachSentinels();
//this.scrollable.container.insertBefore(this.chatInner, this.scrollable.container.lastElementChild); //this.scrollable.container.insertBefore(this.chatInner, this.scrollable.container.lastElementChild);
@ -1305,7 +1338,7 @@ export class AppImManager {
//bubble.remove(); //bubble.remove();
}); });
animationIntersector.checkAnimations(false, ANIMATIONGROUP); animationIntersector.checkAnimations(false, ANIMATION_GROUP);
this.deleteEmptyDateGroups(); this.deleteEmptyDateGroups();
} }
@ -1844,7 +1877,7 @@ export class AppImManager {
isOut: isOut, isOut: isOut,
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
middleware: null, middleware: null,
group: ANIMATIONGROUP group: ANIMATION_GROUP
}); });
preloader.attach(attachmentDiv, false); preloader.attach(attachmentDiv, false);
@ -1956,7 +1989,7 @@ export class AppImManager {
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
isOut, isOut,
group: ANIMATIONGROUP group: ANIMATION_GROUP
}); });
//} //}
} else { } else {
@ -2042,7 +2075,7 @@ export class AppImManager {
div: attachmentDiv, div: attachmentDiv,
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
group: ANIMATIONGROUP, group: ANIMATION_GROUP,
//play: !!message.pending || !multipleRender, //play: !!message.pending || !multipleRender,
play: true, play: true,
loop: true, loop: true,
@ -2078,7 +2111,7 @@ export class AppImManager {
isOut: isOut, isOut: isOut,
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
group: ANIMATIONGROUP group: ANIMATION_GROUP
}); });
} }
@ -2390,7 +2423,7 @@ export class AppImManager {
const realLoadCount = Object.keys(this.bubbles).length > 0 ? Math.max(40, pageCount) : pageCount;//const realLoadCount = 50; const realLoadCount = Object.keys(this.bubbles).length > 0 ? Math.max(40, pageCount) : pageCount;//const realLoadCount = 50;
let loadCount = realLoadCount; let loadCount = realLoadCount;
if(testScroll) { if(TEST_SCROLL) {
//loadCount = 1; //loadCount = 1;
if(Object.keys(this.bubbles).length > 0) if(Object.keys(this.bubbles).length > 0)
return {cached: false, promise: Promise.resolve(true)}; return {cached: false, promise: Promise.resolve(true)};

5
src/lib/appManagers/appMessagesManager.ts

@ -2471,8 +2471,9 @@ export class AppMessagesManager {
let messageWrapped = ''; let messageWrapped = '';
if(text) { if(text) {
if(text.length > 40) { // * 80 for chatlist in landscape orientation
text = text.substr(0, 35) + '...'; if(text.length > 80) {
text = text.substr(0, 75) + '...';
} }
let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true}); let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true});

22
src/lib/appManagers/appSidebarRight.ts

@ -8,7 +8,7 @@ import AppPrivateSearchTab from "../../components/sidebarRight/search";
import AppSharedMediaTab from "../../components/sidebarRight/sharedMedia"; import AppSharedMediaTab from "../../components/sidebarRight/sharedMedia";
import AppForwardTab from "../../components/sidebarRight/forward"; import AppForwardTab from "../../components/sidebarRight/forward";
const COLUMN_ACTIVE_CLASSNAME = 'is-right-column-shown'; export const RIGHT_COLUMN_ACTIVE_CLASSNAME = 'is-right-column-shown';
const sharedMediaTab = new AppSharedMediaTab(); const sharedMediaTab = new AppSharedMediaTab();
const searchTab = new AppPrivateSearchTab(); const searchTab = new AppPrivateSearchTab();
@ -54,7 +54,7 @@ export class AppSidebarRight extends SidebarSlider {
this.gifsTab = gifsTab; this.gifsTab = gifsTab;
mediaSizes.addListener('changeScreen', (from, to) => { mediaSizes.addListener('changeScreen', (from, to) => {
if(from !== undefined && to == ScreenSize.medium) { if(from !== undefined && to == ScreenSize.medium && from !== ScreenSize.mobile) {
this.toggleSidebar(false); this.toggleSidebar(false);
} }
}); });
@ -78,10 +78,10 @@ export class AppSidebarRight extends SidebarSlider {
return res; return res;
} */ } */
public toggleSidebar(enable?: boolean) { public toggleSidebar(enable?: boolean, saveStatus = true) {
/////this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl)); /////this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl));
const active = document.body.classList.contains(COLUMN_ACTIVE_CLASSNAME); const active = document.body.classList.contains(RIGHT_COLUMN_ACTIVE_CLASSNAME);
let willChange: boolean; let willChange: boolean;
if(enable !== undefined) { if(enable !== undefined) {
if(enable) { if(enable) {
@ -97,19 +97,25 @@ export class AppSidebarRight extends SidebarSlider {
if(!willChange) return Promise.resolve(); if(!willChange) return Promise.resolve();
if(saveStatus) {
appImManager.hideRightSidebar = false;
}
if(!active && !this.historyTabIDs.length) { if(!active && !this.historyTabIDs.length) {
this.selectTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia); this.selectTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia);
} }
document.body.classList.toggle(RIGHT_COLUMN_ACTIVE_CLASSNAME, enable);
//console.log('sidebar selectTab', enable, willChange); //console.log('sidebar selectTab', enable, willChange);
if(mediaSizes.isMobile) { if(mediaSizes.isMobile) {
appImManager.selectTab(active ? 1 : 2); appImManager._selectTab(active ? 1 : 2);
return Promise.resolve(); return new Promise(resolve => {
setTimeout(resolve, 250); // delay of slider animation
});
} }
document.body.classList.toggle(COLUMN_ACTIVE_CLASSNAME, enable);
return new Promise(resolve => { return new Promise(resolve => {
setTimeout(resolve, 200); setTimeout(resolve, 200); // delay for third column open
}); });
//return Promise.resolve(); //return Promise.resolve();

18
src/lib/appManagers/appStateManager.ts

@ -7,6 +7,7 @@ import appUsersManager from './appUsersManager';
import apiUpdatesManager from './apiUpdatesManager'; import apiUpdatesManager from './apiUpdatesManager';
import { copy } from '../utils'; import { copy } from '../utils';
import { logger } from '../logger'; import { logger } from '../logger';
import type { AppStickersManager } from './appStickersManager';
const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day
@ -22,14 +23,18 @@ type State = Partial<{
stateCreatedTime: number, stateCreatedTime: number,
recentEmoji: string[], recentEmoji: string[],
topPeers: number[], topPeers: number[],
recentSearch: number[] recentSearch: number[],
stickerSets: AppStickersManager['stickerSets']
}>; }>;
const REFRESH_KEYS = ['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime',
'updates', 'maxSeenMsgID', 'filters', 'topPeers'] as any as Array<keyof State>;
export class AppStateManager { export class AppStateManager {
public loaded: Promise<State>; public loaded: Promise<State>;
private log = logger('STATE'/* , LogLevels.error */); private log = logger('STATE'/* , LogLevels.error */);
private state: State = {}; private state: State;
constructor() { constructor() {
this.loadSavedState(); this.loadSavedState();
@ -37,13 +42,12 @@ export class AppStateManager {
public loadSavedState() { public loadSavedState() {
if(this.loaded) return this.loaded; if(this.loaded) return this.loaded;
return this.loaded = new Promise((resolve, reject) => { return this.loaded = new Promise((resolve) => {
AppStorage.get<State>('state').then((state) => { AppStorage.get<State>('state').then((state) => {
const time = Date.now(); const time = Date.now();
if((state?.stateCreatedTime ?? 0) + REFRESH_EVERY < time) { if((state?.stateCreatedTime ?? 0) + REFRESH_EVERY < time) {
this.log('will refresh state', state.stateCreatedTime, time); this.log('will refresh state', state.stateCreatedTime, time);
(['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime', REFRESH_KEYS.forEach(key => {
'updates', 'maxSeenMsgID', 'filters', 'topPeers'] as any as Array<keyof State>).forEach(key => {
delete state[key]; delete state[key];
}); });
//state = {}; //state = {};
@ -131,10 +135,12 @@ export class AppStateManager {
} }
public getState() { public getState() {
return this.loadSavedState(); return this.state === undefined ? this.loadSavedState() : Promise.resolve(this.state);
} }
public saveState() { public saveState() {
if(this.state === undefined) return;
const messages: any[] = []; const messages: any[] = [];
const dialogs: Dialog[] = []; const dialogs: Dialog[] = [];
const peers = this.state.peers; const peers = this.state.peers;

25
src/lib/appManagers/appStickersManager.ts

@ -1,12 +1,11 @@
import AppStorage from '../storage';
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker'; import apiManager from '../mtproto/mtprotoworker';
import appDocsManager from './appDocsManager'; import appDocsManager from './appDocsManager';
import { $rootScope } from '../utils'; import { $rootScope } from '../utils';
import { StickerSet, InputStickerSet, StickerSetCovered, MessagesRecentStickers, Document, InputFileLocation, MessagesStickerSet, PhotoSize } from '../../layer'; import { StickerSet, InputStickerSet, StickerSetCovered, MessagesRecentStickers, Document, InputFileLocation, MessagesStickerSet, PhotoSize } from '../../layer';
import { Modify } from '../../types'; import { Modify } from '../../types';
import appStateManager from './appStateManager';
class AppStickersManager { export class AppStickersManager {
private stickerSets: { private stickerSets: {
[stickerSetID: string]: MessagesStickerSet [stickerSetID: string]: MessagesStickerSet
} = {}; } = {};
@ -27,14 +26,14 @@ class AppStickersManager {
}; };
constructor() { constructor() {
AppStorage.get<AppStickersManager['stickerSets']>('stickerSets').then((sets) => { appStateManager.getState().then(({stickerSets}) => {
if(sets) { if(stickerSets) {
for(let id in sets) { for(let id in stickerSets) {
let set = sets[id]; let set = stickerSets[id];
this.saveStickers(set.documents); this.saveStickers(set.documents);
} }
this.stickerSets = sets; this.stickerSets = stickerSets;
} }
//if(!this.stickerSets['emoji']) { //if(!this.stickerSets['emoji']) {
@ -130,9 +129,8 @@ class AppStickersManager {
} }
} }
AppStorage.set({ appStateManager.pushToState('stickerSets', savedSets);
stickerSets: savedSets appStateManager.saveState();
});
this.saveSetsTimeout = 0; this.saveSetsTimeout = 0;
}, 100); }, 100);
@ -258,14 +256,9 @@ class AppStickersManager {
return hashed.result.concat(foundSaved); return hashed.result.concat(foundSaved);
} }
public async cleanup() { // if logout
await AppStorage.remove('stickerSets');
}
} }
const appStickersManager = new AppStickersManager(); const appStickersManager = new AppStickersManager();
// @ts-ignore
if(process.env.NODE_ENV != 'production') { if(process.env.NODE_ENV != 'production') {
(window as any).appStickersManager = appStickersManager; (window as any).appStickersManager = appStickersManager;
} }

67
src/lib/lottieLoader.ts

@ -1,7 +1,6 @@
import { logger, LogLevels } from "./logger"; import { logger, LogLevels } from "./logger";
import animationIntersector from "../components/animationIntersector"; import animationIntersector from "../components/animationIntersector";
import apiManager from "./mtproto/mtprotoworker"; import apiManager from "./mtproto/mtprotoworker";
import { copy } from "./utils";
import EventListenerBase from "../helpers/eventListenerBase"; import EventListenerBase from "../helpers/eventListenerBase";
import mediaSizes from "../helpers/mediaSizes"; import mediaSizes from "../helpers/mediaSizes";
import { isApple, isSafari } from "../helpers/userAgent"; import { isApple, isSafari } from "../helpers/userAgent";
@ -21,7 +20,8 @@ type RLottieOptions = {
height?: number, height?: number,
group?: string, group?: string,
noCache?: true, noCache?: true,
needUpscale?: true needUpscale?: true,
skipRatio?: number
}; };
export class RLottiePlayer extends EventListenerBase<{ export class RLottiePlayer extends EventListenerBase<{
@ -36,6 +36,7 @@ export class RLottiePlayer extends EventListenerBase<{
public curFrame: number; public curFrame: number;
public frameCount: number; public frameCount: number;
public fps: number; public fps: number;
public skipDelta: number;
public worker: QueryableWorker; public worker: QueryableWorker;
@ -89,6 +90,16 @@ export class RLottiePlayer extends EventListenerBase<{
} }
} }
// Skip ratio
let skipRatio: number;
if(options.skipRatio !== undefined) skipRatio = options.skipRatio;
else if(mediaSizes.isMobile && this.width < 100 && this.height < 100) {
skipRatio = 0.5;
}
this.skipDelta = skipRatio !== undefined ? 1 / skipRatio | 0 : 1;
// Pixel ratio
const pixelRatio = window.devicePixelRatio; const pixelRatio = window.devicePixelRatio;
if(pixelRatio > 1) { if(pixelRatio > 1) {
//this.cachingEnabled = true;//this.width < 100 && this.height < 100; //this.cachingEnabled = true;//this.width < 100 && this.height < 100;
@ -108,8 +119,9 @@ export class RLottiePlayer extends EventListenerBase<{
} }
} }
// Cache frames params
if(!options.noCache) { if(!options.noCache) {
// проверка на размер уже после скейлинга, сделано для попапа и сайдбарfа, где стикеры 80х80 и 68х68, туда нужно 75% // проверка на размер уже после скейлинга, сделано для попапа и сайдбара, где стикеры 80х80 и 68х68, туда нужно 75%
if(isApple && this.width > 100 && this.height > 100) { if(isApple && this.width > 100 && this.height > 100) {
this.cachingDelta = 2; //2 // 50% this.cachingDelta = 2; //2 // 50%
} else if(this.width < 100 && this.height < 100) { } else if(this.width < 100 && this.height < 100) {
@ -146,12 +158,8 @@ export class RLottiePlayer extends EventListenerBase<{
this.worker.sendQuery(methodName, this.reqId, ...args); this.worker.sendQuery(methodName, this.reqId, ...args);
} }
public loadFromData(json: any) { public loadFromData(jsonString: string) {
this.sendQuery('loadFromData', json, this.width, this.height, { this.sendQuery('loadFromData', jsonString, this.width, this.height);
paused: this.paused,
direction: this.direction,
speed: this.speed
});
} }
public play() { public play() {
@ -276,7 +284,10 @@ export class RLottiePlayer extends EventListenerBase<{
} }
private mainLoopForwards() { private mainLoopForwards() {
this.requestFrame(this.curFrame++); const frame = this.curFrame;
this.curFrame += this.skipDelta;
this.requestFrame(frame);
if(this.curFrame >= this.frameCount) { if(this.curFrame >= this.frameCount) {
//this.playedTimes++; //this.playedTimes++;
@ -292,7 +303,10 @@ export class RLottiePlayer extends EventListenerBase<{
} }
private mainLoopBackwards() { private mainLoopBackwards() {
this.requestFrame(this.curFrame--); const frame = this.curFrame;
this.curFrame -= this.skipDelta;
this.requestFrame(frame);
if(this.curFrame < 0) { if(this.curFrame < 0) {
//this.playedTimes++; //this.playedTimes++;
@ -311,7 +325,7 @@ export class RLottiePlayer extends EventListenerBase<{
//window.cancelAnimationFrame(this.rafId); //window.cancelAnimationFrame(this.rafId);
clearTimeout(this.rafId); clearTimeout(this.rafId);
this.frInterval = 1000 / this.fps / this.speed; this.frInterval = 1000 / this.fps / this.speed * this.skipDelta;
this.frThen = Date.now() - this.frInterval; this.frThen = Date.now() - this.frInterval;
//console.trace('setMainLoop', this.frInterval, this.direction, this, JSON.stringify(this.listenerResults), this.listenerResults); //console.trace('setMainLoop', this.frInterval, this.direction, this, JSON.stringify(this.listenerResults), this.listenerResults);
@ -337,7 +351,7 @@ export class RLottiePlayer extends EventListenerBase<{
this.curFrame = this.direction == 1 ? 0 : frameCount - 1; this.curFrame = this.direction == 1 ? 0 : frameCount - 1;
this.frameCount = frameCount; this.frameCount = frameCount;
this.fps = fps; this.fps = fps;
this.frInterval = 1000 / this.fps / this.speed; this.frInterval = 1000 / this.fps / this.speed * this.skipDelta;
this.frThen = Date.now() - this.frInterval; this.frThen = Date.now() - this.frInterval;
//this.sendQuery('renderFrame', 0); //this.sendQuery('renderFrame', 0);
@ -522,9 +536,6 @@ class LottieLoader {
if(this.loadPromise) return this.loadPromise; if(this.loadPromise) return this.loadPromise;
const onFrame = this.onFrame.bind(this);
const onPlayerLoaded = this.onPlayerLoaded.bind(this);
return this.loadPromise = new Promise((resolve, reject) => { return this.loadPromise = new Promise((resolve, reject) => {
let remain = this.workersLimit; let remain = this.workersLimit;
for(let i = 0; i < this.workersLimit; ++i) { for(let i = 0; i < this.workersLimit; ++i) {
@ -533,8 +544,9 @@ class LottieLoader {
worker.addListener('ready', () => { worker.addListener('ready', () => {
this.log('worker #' + i + ' ready'); this.log('worker #' + i + ' ready');
worker.addListener('frame', onFrame); worker.addListener('frame', this.onFrame);
worker.addListener('loaded', onPlayerLoaded); worker.addListener('loaded', this.onPlayerLoaded);
worker.addListener('error', this.onPlayerError);
--remain; --remain;
if(!remain) { if(!remain) {
@ -630,7 +642,7 @@ class LottieLoader {
return player; return player;
} }
private onPlayerLoaded(reqId: number, frameCount: number, fps: number) { private onPlayerLoaded = (reqId: number, frameCount: number, fps: number) => {
const rlPlayer = this.players[reqId]; const rlPlayer = this.players[reqId];
if(!rlPlayer) { if(!rlPlayer) {
this.log.warn('onPlayerLoaded on destroyed player:', reqId, frameCount); this.log.warn('onPlayerLoaded on destroyed player:', reqId, frameCount);
@ -642,9 +654,9 @@ class LottieLoader {
//rlPlayer.addListener('firstFrame', () => { //rlPlayer.addListener('firstFrame', () => {
//animationIntersector.addAnimation(player, group); //animationIntersector.addAnimation(player, group);
//}, true); //}, true);
} };
private onFrame(reqId: number, frameNo: number, frame: Uint8ClampedArray) { private onFrame = (reqId: number, frameNo: number, frame: Uint8ClampedArray) => {
const rlPlayer = this.players[reqId]; const rlPlayer = this.players[reqId];
if(!rlPlayer) { if(!rlPlayer) {
this.log.warn('onFrame on destroyed player:', reqId, frameNo); this.log.warn('onFrame on destroyed player:', reqId, frameNo);
@ -653,7 +665,18 @@ class LottieLoader {
rlPlayer.clamped = frame; rlPlayer.clamped = frame;
rlPlayer.renderFrame(frame, frameNo); rlPlayer.renderFrame(frame, frameNo);
} };
private onPlayerError = (reqId: number, error: Error) => {
const rlPlayer = this.players[reqId];
if(rlPlayer) {
// ! will need refactoring later, this is not the best way to remove the animation
const animations = animationIntersector.getAnimations(rlPlayer.el);
animations.forEach(animation => {
animationIntersector.checkAnimation(animation, true, true);
});
}
};
public onDestroy(reqId: number) { public onDestroy(reqId: number) {
delete this.players[reqId]; delete this.players[reqId];

17
src/lib/rlottie/rlottie.worker.ts

@ -32,6 +32,7 @@ export class RLottieItem {
worker.Api.resize(this.handle, this.width, this.height); worker.Api.resize(this.handle, this.width, this.height);
} catch(e) { } catch(e) {
console.error('init RLottieItem error:', e); console.error('init RLottieItem error:', e);
reply('error', this.reqId, e);
} }
} }
@ -60,6 +61,7 @@ export class RLottieItem {
} catch(e) { } catch(e) {
console.error('Render error:', e); console.error('Render error:', e);
this.dead = true; this.dead = true;
reply('error', this.reqId, e);
} }
} }
@ -96,8 +98,8 @@ Module.onRuntimeInitialized = function() {
worker.init(); worker.init();
}; };
var items: {[reqId: string]: RLottieItem} = {}; const items: {[reqId: string]: RLottieItem} = {};
var queryableFunctions = { const queryableFunctions = {
loadFromData: function(reqId: number, jsString: string, width: number, height: number) { loadFromData: function(reqId: number, jsString: string, width: number, height: number) {
try { try {
// ! WARNING, с этой проверкой не все стикеры работают, например - ДУРКА // ! WARNING, с этой проверкой не все стикеры работают, например - ДУРКА
@ -108,14 +110,19 @@ var queryableFunctions = {
const match = jsString.match(/"fr":\s*?(\d+?),/); const match = jsString.match(/"fr":\s*?(\d+?),/);
const frameRate = +match?.[1] || DEFAULT_FPS; const frameRate = +match?.[1] || DEFAULT_FPS;
console.log('Rendering sticker:', reqId, frameRate, 'now rendered:', Object.keys(items).length); //console.log('Rendering sticker:', reqId, frameRate, 'now rendered:', Object.keys(items).length);
items[reqId] = new RLottieItem(reqId, jsString, width, height, frameRate); items[reqId] = new RLottieItem(reqId, jsString, width, height, frameRate);
} catch(e) { } catch(e) {
console.error('Invalid file for sticker:', jsString); console.error('Invalid file for sticker:', jsString);
reply('error', reqId, e);
} }
}, },
destroy: function(reqId: number) { destroy: function(reqId: number) {
if(!items.hasOwnProperty(reqId)) {
return;
}
items[reqId].destroy(); items[reqId].destroy();
delete items[reqId]; delete items[reqId];
}, },
@ -143,10 +150,10 @@ function defaultReply(message: any) {
* let the calling scope pass in the global scope object. * let the calling scope pass in the global scope object.
* @returns {boolean} * @returns {boolean}
*/ */
var _isSafari: boolean = null; let _isSafari: boolean = null;
function isSafari(scope: any) { function isSafari(scope: any) {
if(_isSafari == null) { if(_isSafari == null) {
var userAgent = scope.navigator ? scope.navigator.userAgent : null; const userAgent = scope.navigator ? scope.navigator.userAgent : null;
_isSafari = !!scope.safari || _isSafari = !!scope.safari ||
!!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome')))); !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))));
} }

67
src/scss/partials/_chat.scss

@ -34,7 +34,7 @@
border-left: 1px solid #DADCE0; border-left: 1px solid #DADCE0;
border-right: 1px solid #DADCE0; border-right: 1px solid #DADCE0;
.sidebar-close-button, .menu-search { .menu-search {
display: none; display: none;
} }
@ -45,6 +45,12 @@
} }
} }
@include respond-to(no-floating-left-sidebar) {
.sidebar-close-button {
display: none;
}
}
/* @include respond-to(wide-screens) { /* @include respond-to(wide-screens) {
transition: .2s ease-in-out; transition: .2s ease-in-out;
align-self: start; align-self: start;
@ -203,11 +209,22 @@
#chat-input { #chat-input {
display: flex; display: flex;
width: 100%; width: 100%;
max-width: var(--messages-container-width); max-width: 100%;
padding: .25rem 1rem 0; padding-top: .25rem;
flex-direction: column; flex-direction: column;
flex: 0 0 auto; /* Forces side columns to stay same width */ flex: 0 0 auto; /* Forces side columns to stay same width */
position: relative; position: relative;
//overflow: hidden;
/* // * for no ESG top
flex: 1 1 auto;
height: calc(100% - 56px); */
@include respond-to(esg-top) {
/* flex: 0 0 auto;
height: auto; */
max-width: var(--messages-container-width);
}
@include respond-to(medium-screens) { @include respond-to(medium-screens) {
width: calc(100% - var(--right-column-width)); width: calc(100% - var(--right-column-width));
@ -222,21 +239,23 @@
} }
} }
@include respond-to(handhelds) {
padding-left: .5rem;
padding-right: .5rem;
}
.chat-input-container { .chat-input-container {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
max-width: var(--messages-container-width);
margin: 0 auto;
width: 100%;
padding: 0 .5rem;
flex: 0 0 auto;
@include respond-to(handhelds) { @include respond-to(handhelds) {
padding-bottom: .5rem; padding-bottom: .5rem;
} }
@include respond-to(not-handhelds) { @include respond-to(not-handhelds) {
padding-left: 1rem;
padding-right: 1rem;
padding-bottom: 21px; padding-bottom: 21px;
} }
} }
@ -313,6 +332,10 @@
} }
} }
.btn-icon {
transition: .2s color, background-color .2s;
}
#btn-record-cancel, #btn-send { #btn-record-cancel, #btn-send {
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1.5rem; line-height: 1.5rem;
@ -452,6 +475,24 @@
position: relative; position: relative;
flex: 3; flex: 3;
@include respond-to(floating-left-sidebar) {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
transform: translateZ(0);
transition: transform var(--layer-transition);
body.is-left-column-shown & {
transform: translate3d(26.5rem, 0, 0);
.sidebar-close-button {
transform: rotate(180deg);
}
}
}
.chat-background { .chat-background {
overflow: hidden; overflow: hidden;
@ -595,7 +636,6 @@
.btn-icon { .btn-icon {
display: block; display: block;
transition: .2s color, background-color .2s;
flex: 0 0 auto; flex: 0 0 auto;
font-size: 24px; font-size: 24px;
line-height: 24px; line-height: 24px;
@ -619,7 +659,7 @@
position: absolute !important; position: absolute !important;
top: 100%; top: 100%;
width: 100% !important; width: 100% !important;
background: #fff; background: #fff !important;
left: 0; left: 0;
// border-top: 1px solid #ccc; // border-top: 1px solid #ccc;
max-height: 100% !important; max-height: 100% !important;
@ -916,9 +956,9 @@
//overflow: hidden; //overflow: hidden;
position: relative; position: relative;
html.is-safari & { /* html.is-safari & > .scrollable {
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow
} } */
// ! WARNING, НЕЛЬЗЯ СТАВИТЬ ТРАНСФОРМ КРОМЕ TRANSLATEZ(0) НА БЛОК С OVERFLOW, ОН БУДЕТ ПРЫГАТЬ ВВЕРХ ПРИ ВКЛЮЧЕННОМ ПРАВИЛЕ И ЭТО НЕ ИСПРАВИТЬ JS'ОМ! // ! WARNING, НЕЛЬЗЯ СТАВИТЬ ТРАНСФОРМ КРОМЕ TRANSLATEZ(0) НА БЛОК С OVERFLOW, ОН БУДЕТ ПРЫГАТЬ ВВЕРХ ПРИ ВКЛЮЧЕННОМ ПРАВИЛЕ И ЭТО НЕ ИСПРАВИТЬ JS'ОМ!
@include respond-to(medium-screens) { @include respond-to(medium-screens) {
@ -1117,7 +1157,8 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
box-shadow: 0px -1px 5px -1px rgba(0,0,0,0.21); box-shadow: 0px -1px 5px -1px rgba(0, 0, 0, .21);
overflow: hidden;
.chat-search-count { .chat-search-count {
margin-left: 8px; margin-left: 8px;

10
src/scss/partials/_chatBubble.scss

@ -797,10 +797,6 @@ $bubble-margin: .25rem;
margin-right: -2px; margin-right: -2px;
} }
.time {
width: unset;
}
/* @include respond-to(handhelds) { /* @include respond-to(handhelds) {
.preloader-container .you-spin-me-round { .preloader-container .you-spin-me-round {
margin-top: 1px; margin-top: 1px;
@ -809,6 +805,12 @@ $bubble-margin: .25rem;
} */ } */
} }
.bubble__container .message.audio-message {
.time {
width: unset !important;
}
}
.message.contact-message { .message.contact-message {
min-width: 200px; min-width: 200px;
padding-left: 8px; padding-left: 8px;

242
src/scss/partials/_emojiDropdown.scss

@ -2,35 +2,37 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
height: 282px; //height: 282px;
height: unquote('min(282px, calc(var(--vh) * 100 - 135px))');
background: #fff; background: #fff;
display: flex;
overflow: hidden; overflow: hidden;
flex: 0 0 auto; flex: 1 1 auto;
max-height: 100%;
@include respond-to(not-handhelds) { @include respond-to(esg-top) {
position: absolute !important; position: absolute !important;
left: 1rem; left: 1rem;
bottom: calc(85px); bottom: calc(85px);
width: 420px !important; width: 420px !important;
height: 420px; height: 420px;
box-shadow: 0px 5px 10px 5px rgba(16, 35, 47, 0.14); max-height: 420px;
box-shadow: 0px 5px 10px 5px rgba(16, 35, 47, .14);
z-index: 3; z-index: 3;
border-radius: 10px; border-radius: 10px;
transition: all 0.2s ease-out; transition: all .2s ease-out;
transform: scale(0); transform: scale(0);
transform-origin: 0 100%; transform-origin: 0 100%;
&.active { &.active {
transition: all 0.2s ease-in; transition: all .2s ease-in;
transform: scale(1); transform: scale(1);
} }
} }
@include respond-to(handhelds) { /* @include respond-to(handhelds) {
width: calc(100% + 1rem); width: calc(100% + 1rem);
margin-left: -.5rem; margin-left: -.5rem;
} } */
> .menu-horizontal { > .menu-horizontal {
//font-weight: 500; //font-weight: 500;
@ -92,112 +94,6 @@
} }
} }
.emoji-category {
//padding-top: 1px;
position: relative;
.category-items {
display: grid;
grid-column-gap: 2.44px;
grid-template-columns: repeat(9, 1fr);
font-size: 2.25rem;
line-height: 2.25rem;
> span {
margin: 0;
padding: 4px 4px;
line-height: inherit;
border-radius: 8px;
cursor: pointer;
user-select: none;
width: 42px;
height: 42px;
.emoji {
width: 100%;
height: 100%;
vertical-align: unset;
}
html.no-touch &:hover {
background-color: rgba(112, 117, 121, 0.08);
}
}
}
&:first-child {
//padding-top: 5px;
}
/* &::after {
content: "";
flex: auto;
} */
}
.sticker-category {
position: relative;
&::after {
content: "";
flex: auto;
}
/* &.not-full::after {
content: "";
flex: auto;
} */
.category-items {
width: 100%;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-column-gap: 1px;
> div {
width: 80px;
height: 80px;
display: flex;
align-items: center;
/* overflow: hidden; */
cursor: pointer;
user-select: none;
/* margin: 3.5px 0;
margin-right: 6.25px; */
padding: 1px 2.5px;
justify-content: center;
border-radius: 12px;
padding: 0;
@include respond-to(handhelds) {
// width: 64px;
//height: 64px;
height: 75px;
justify-self: center;
padding: 0;
min-width: 64px;
width: 100%;
}
html.no-touch &:hover {
background-color: rgba(112, 117, 121, 0.08);
}
/* &:nth-child(5n+5) {
margin-right: 0;
} */
> img {
max-width: 100%;
max-height: 100%;
animation: fadeIn .2s ease forwards;
}
}
}
}
> div { > div {
min-height: 100%; min-height: 100%;
/* display: flex; */ /* display: flex; */
@ -221,12 +117,16 @@
flex: unset; flex: unset;
padding: 0; padding: 0;
} }
.category-items > span { .category-items {
width: 40px; grid-template-columns: repeat(auto-fill, 40px);
height: 40px;
justify-self: center; > span {
} width: 40px;
height: 40px;
justify-self: center;
}
}
.category-title { .category-title {
padding: 12px 6px 6px 10px; padding: 12px 6px 6px 10px;
@ -238,7 +138,6 @@
.emoji-category .category-items { .emoji-category .category-items {
grid-column-gap: unset; grid-column-gap: unset;
grid-template-columns: repeat(8, 1fr);
} }
} }
} }
@ -247,7 +146,7 @@
.menu-horizontal { .menu-horizontal {
height: 48px; height: 48px;
border-bottom: none; border-bottom: none;
padding: 2px 2px 2px 2px; padding: 2px;
width: 100%; width: 100%;
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, 0.21); box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, 0.21);
z-index: 4; z-index: 4;
@ -258,6 +157,97 @@
} }
} }
.emoji-category {
//padding-top: 1px;
position: relative;
.category-items {
display: grid;
grid-column-gap: 2.44px;
grid-template-columns: repeat(auto-fill, 42px);
justify-content: space-between;
font-size: 2.25rem;
line-height: 2.25rem;
> span {
margin: 0;
padding: 4px 4px;
line-height: inherit;
border-radius: 8px;
cursor: pointer;
user-select: none;
width: 42px;
height: 42px;
.emoji {
width: 100%;
height: 100%;
vertical-align: unset;
}
html.no-touch &:hover {
background-color: rgba(112, 117, 121, .08);
}
}
}
/* &:first-child {
//padding-top: 5px;
} */
/* &::after {
content: "";
flex: auto;
} */
}
.sticker-category {
position: relative;
/* &::after {
content: "";
flex: auto;
} */
/* &.not-full::after {
content: "";
flex: auto;
} */
.category-items {
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fill, var(--esg-sticker-size)); // 64px
grid-column-gap: 1px;
justify-content: space-between;
> .grid-item {
html.no-touch &:hover {
border-radius: 12px;
background-color: rgba(112, 117, 121, .08);
}
/* &:nth-child(5n+5) {
margin-right: 0;
} */
> img, > .rlottie {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
> img {
animation: fadeIn .2s ease forwards;
}
}
}
}
.menu-horizontal { .menu-horizontal {
li { li {
border-radius: 50%; border-radius: 50%;
@ -273,7 +263,7 @@
.menu-horizontal { .menu-horizontal {
width: 100%; width: 100%;
height: 48px; height: 48px;
box-shadow: 0px -2px 5px -1px rgba(0, 0, 0, 0.21); box-shadow: 0px -2px 5px -1px rgba(0, 0, 0, .21);
li { li {
font-size: 1.5rem; font-size: 1.5rem;
@ -291,7 +281,7 @@
&.active { &.active {
.scrollable { .scrollable {
padding: 0; padding: 0;
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, 0.21); box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .21);
} }
.menu-horizontal { .menu-horizontal {
@ -320,7 +310,7 @@
&.active { &.active {
&:not(.tgico-recent) { &:not(.tgico-recent) {
background-color: rgba(112, 117, 121, 0.08); background-color: rgba(112, 117, 121, .08);
} }
} }

35
src/scss/partials/_leftSidebar.scss

@ -6,6 +6,30 @@
// ! -.5 because of border-left and border-right on whole page // ! -.5 because of border-left and border-right on whole page
max-width: calc(#{$large-screen} / 4); max-width: calc(#{$large-screen} / 4);
@include respond-to(handhelds) {
width: 100%;
max-width: 100%;
}
@include respond-to(floating-left-sidebar) {
display: flex;
position: fixed;
left: 0;
top: 0;
height: calc(var(--vh, 1vh) * 100);
width: 26.5rem;
transform: translate3d(-5rem, 0, 0);
transition: transform var(--layer-transition);
body.is-left-column-shown & {
transform: translateZ(0);
}
}
@include respond-to(no-floating-left-sidebar) {
display: flex;
}
@include respond-to(before-medium-screens) { @include respond-to(before-medium-screens) {
flex: 2; flex: 2;
} }
@ -243,18 +267,15 @@
content: $tgico-close; content: $tgico-close;
} }
@include respond-to(handhelds) {
transform: translateY(0px);
position: fixed !important;
}
}
.btn-menu-toggle {
@include respond-to(handhelds) { @include respond-to(handhelds) {
width: 54px; width: 54px;
height: 54px; height: 54px;
bottom: 14px; bottom: 14px;
right: 14px; right: 14px;
transform: translateY(0px);
position: fixed !important;
z-index: 1;
} }
} }

22
src/scss/partials/_rightSidebar.scss

@ -89,10 +89,10 @@
flex: 1 1 auto; flex: 1 1 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: 13px; padding-bottom: 13px;
@include respond-to(not-handhelds) { @include respond-to(not-handhelds) {
margin-top: 15px; padding-top: 15px;
} }
} }
@ -267,6 +267,7 @@
> div { > div {
//height: 100%; //height: 100%;
position: relative; position: relative;
min-height: 150px;
/* > div:not(:empty) + .content-empty { /* > div:not(:empty) + .content-empty {
display: none; display: none;
@ -301,15 +302,6 @@
padding: 7.5px 7.5px 7.5px 6.5px; padding: 7.5px 7.5px 7.5px 6.5px;
} }
.media-item {
height: 0;
padding-bottom: 100%;
overflow: hidden;
position: relative;
cursor: pointer;
//background-color: #000;
}
.video-time { .video-time {
position: absolute; position: absolute;
left: 5px; left: 5px;
@ -323,13 +315,7 @@
color: white; color: white;
} }
.media-image { .grid-item-media {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
opacity: 1; opacity: 1;
transition: opacity .2s ease; transition: opacity .2s ease;

2
src/scss/partials/_ripple.scss

@ -74,7 +74,7 @@
} }
} }
@include respond-to(handhelds) { @include respond-to(until-floating-left-sidebar) {
.chats-container ul li > .rp { .chats-container ul li > .rp {
.c-ripple { .c-ripple {
--ripple-duration: .2s; --ripple-duration: .2s;

1
src/scss/partials/_sidebar.scss

@ -40,6 +40,7 @@
&-close-button { &-close-button {
padding-left: 10px; padding-left: 10px;
overflow: inherit !important;
} }
&-content { &-content {

20
src/scss/partials/pages/_chats.scss

@ -61,6 +61,26 @@
display: flex; display: flex;
} }
} }
@include respond-to(floating-left-sidebar) {
.main-column {
height: calc(var(--vh, 1vh) * 100) !important;
}
}
/* @include respond-to(until-floating-left-sidebar) {
.main-column {
display: flex !important;
&.active {
z-index: 3;
}
&.to {
z-index: 4;
}
}
} */
.preloader { .preloader {
width: 50px; width: 50px;

55
src/scss/style.scss

@ -17,12 +17,14 @@ $lightgrey: #dadce0;
$light: rgba($color-gray, 0.08); $light: rgba($color-gray, 0.08);
//$small-screen: 720px; //$small-screen: 720px;
$small-screen: 896px; $small-screen: 600px;
//$small-screen: 900px; //$small-screen: 900px;
$medium-screen: 1275px; $medium-screen: 1275px;
$large-screen: 1680px; $large-screen: 1680px;
//$large-screen: 16800px; //$large-screen: 16800px;
$floating-left-sidebar: 925px;
@mixin respond-to($media) { @mixin respond-to($media) {
@if $media == handhelds { @if $media == handhelds {
@media only screen and (max-width: $small-screen) { @content; } @media only screen and (max-width: $small-screen) { @content; }
@ -49,6 +51,20 @@ $large-screen: 1680px;
@else if $media == not-handhelds { @else if $media == not-handhelds {
@media only screen and (min-width: $small-screen + 1) { @content; } @media only screen and (min-width: $small-screen + 1) { @content; }
} }
@else if $media == floating-left-sidebar {
@media only screen and (min-width: $small-screen + 1) and (max-width: $floating-left-sidebar) { @content; }
}
@else if $media == until-floating-left-sidebar {
@media only screen and (max-width: $floating-left-sidebar) { @content; }
}
@else if $media == no-floating-left-sidebar {
@media only screen and (min-width: $floating-left-sidebar + 1) { @content; }
}
@else if $media == esg-top { // topbar + chat input + margin bottom + height of ESG
@media only screen and (min-height: 570px) and (min-width: $small-screen + 1) { @content; }
}
} }
:root { :root {
@ -61,9 +77,11 @@ $large-screen: 1680px;
--message-beside-button-margin: 2.875rem; --message-beside-button-margin: 2.875rem;
--message-time-background: rgba(0, 0, 0, .35); --message-time-background: rgba(0, 0, 0, .35);
--messages-container-width: 728px; --messages-container-width: 728px;
--esg-sticker-size: 80px;
@include respond-to(handhelds) { @include respond-to(handhelds) {
--right-column-width: 100vw; --right-column-width: 100vw;
--esg-sticker-size: 68px;
} }
@include respond-to(not-handhelds) { @include respond-to(not-handhelds) {
@ -173,9 +191,9 @@ html, body {
width: 100%; width: 100%;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
@include respond-to(handhelds) { //@include respond-to(handhelds) {
height: calc(var(--vh, 1vh) * 100); height: calc(var(--vh, 1vh) * 100);
} //}
/* @include respond-to(handhelds) { /* @include respond-to(handhelds) {
//overflow-y: auto; //overflow-y: auto;
@ -228,8 +246,8 @@ input, textarea, button, select, a, div {
//} //}
} }
.disable-hover, .disable-hover/* ,
.disable-hover * { .disable-hover * */ {
pointer-events: none !important; pointer-events: none !important;
} }
@ -676,16 +694,12 @@ hr {
display: inline-block; display: inline-block;
height: 25px; height: 25px;
line-height: 25px; line-height: 25px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
&:before, &:after { &:before, &:after {
content: ''; content: '';
left: 0; left: 0;
position: absolute; position: absolute;
-webkit-transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;
transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s; transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;
} }
@ -711,9 +725,7 @@ hr {
border: 2px solid transparent; border: 2px solid transparent;
left: 6px; left: 6px;
top: 10px; top: 10px;
-webkit-transform: rotateZ(45deg);
transform: rotateZ(45deg); transform: rotateZ(45deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%; transform-origin: 100% 100%;
} }
@ -726,9 +738,7 @@ hr {
border-left: 2px solid transparent; border-left: 2px solid transparent;
border-right: 2px solid #fff; border-right: 2px solid #fff;
border-bottom: 2px solid #fff; border-bottom: 2px solid #fff;
-webkit-transform: rotateZ(45deg);
transform: rotateZ(45deg); transform: rotateZ(45deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%; transform-origin: 100% 100%;
} }
@ -1132,6 +1142,25 @@ img.emoji {
} }
} }
.grid-item {
height: 0;
padding-bottom: 100%;
overflow: hidden;
position: relative;
cursor: pointer;
user-select: none;
&-media {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
}
}
// *:not(input):not(textarea) { // *:not(input):not(textarea) {
// -webkit-user-select: none; /* disable selection/Copy of UIWebView */ // -webkit-user-select: none; /* disable selection/Copy of UIWebView */
// -webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */ // -webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */

Loading…
Cancel
Save