From 198eea41ee05940f49e2ab6c9d9d7c2fc74f0456 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sun, 18 Apr 2021 15:55:56 +0400 Subject: [PATCH] Fix moving avatars after chat jump Handle instant peer changing correctly Display unread badge only in chats Delay opening on ESG hover Lazy load sticker sets thumbs Fix onchanging profile info flick Hide shared media menu tabs Scroll on click to shared media tab Fix media tabs scroll position Fix voice messages playback in search on iOS Hover & play on waveform Better input fields border animation Don't lock scroll on horizontal scrollables if unnecessary Restrict editing & deleting outgoing messages Display avatars in notifications Open chat on notification click Don't close country selector on scrolling Select single country by enter Fix jumping text in context menus Changed preloader color --- README.md | 4 +- src/components/appSearchSuper..ts | 93 ++++++++++++------- src/components/audio.ts | 7 +- src/components/chat/bubbles.ts | 18 +++- src/components/chat/chat.ts | 2 + src/components/chat/input.ts | 9 +- src/components/emoticonsDropdown/index.ts | 42 +++++---- .../emoticonsDropdown/tabs/stickers.ts | 61 ++++++------ src/components/horizontalMenu.ts | 9 +- src/components/inputField.ts | 6 ++ src/components/inputSearch.ts | 2 +- src/components/scrollable.ts | 4 +- .../sidebarRight/tabs/sharedMedia.ts | 4 +- src/config/app.ts | 4 +- src/lang.ts | 9 ++ src/lib/appManagers/appChatsManager.ts | 4 +- src/lib/appManagers/appDialogsManager.ts | 1 + src/lib/appManagers/appImManager.ts | 5 +- src/lib/appManagers/appMessagesManager.ts | 72 +++++--------- .../appManagers/appNotificationsManager.ts | 10 +- src/lib/appManagers/appProfileManager.ts | 28 +++--- src/lib/mtproto/networker.ts | 23 +++-- src/lib/mtproto/serverTimeManager.ts | 5 +- src/lib/rootScope.ts | 2 +- src/pages/pageAuthCode.ts | 3 +- src/pages/pageSignIn.ts | 67 ++++++------- src/scss/partials/_audio.scss | 4 + src/scss/partials/_button.scss | 4 +- src/scss/partials/_chat.scss | 12 +-- src/scss/partials/_chatBubble.scss | 4 +- src/scss/partials/_input.scss | 50 +++++++--- src/scss/partials/_preloader.scss | 2 +- src/scss/partials/_rightSidebar.scss | 5 + src/scss/partials/popups/_createPoll.scss | 13 ++- 34 files changed, 347 insertions(+), 241 deletions(-) diff --git a/README.md b/README.md index 366dceba..68ce59dd 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ Source maps are included in production build for your convenience. Should be applied like that: http://localhost:8080/?test=1 -### Troubleshooting +### Troubleshooting & Suggesting -If you find an issue with this app, let Telegram know using the [Suggestions Platform](https://bugs.telegram.org/c/4002). +If you find an issue with this app or wish something to be added, let Telegram know using the [Suggestions Platform](https://bugs.telegram.org/c/4002). ### Licensing diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index ad27e06a..eeaea68d 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -70,10 +70,10 @@ export default class AppSearchSuper { public tabs: {[t in SearchSuperType]: HTMLDivElement} = {} as any; public mediaTab: SearchSuperMediaTab; - public tabSelected: HTMLElement; public container: HTMLElement; public nav: HTMLElement; + private navScrollableContainer: HTMLDivElement; private tabsContainer: HTMLElement; private tabsMenu: HTMLElement; private prevTabId = -1; @@ -112,6 +112,8 @@ export default class AppSearchSuper { private membersList: SortedUserList; + private skipScroll: boolean; + // * arguments public mediaTabs: SearchSuperMediaTab[]; public scrollable: Scrollable; @@ -128,7 +130,7 @@ export default class AppSearchSuper { this.container = document.createElement('div'); this.container.classList.add('search-super'); - const navScrollableContainer = document.createElement('div'); + const navScrollableContainer = this.navScrollableContainer = document.createElement('div'); navScrollableContainer.classList.add('search-super-tabs-scrollable', 'menu-horizontal-scrollable', 'sticky'); const navScrollable = new ScrollableX(navScrollableContainer); @@ -185,48 +187,67 @@ export default class AppSearchSuper { this.searchGroupMedia = new SearchGroup('', 'messages', true); this.scrollable.onScrolledBottom = () => { - if(this.tabSelected && this.tabSelected.childElementCount/* && false */) { + if(this.mediaTab.contentTab && this.mediaTab.contentTab.childElementCount/* && false */) { //this.log('onScrolledBottom will load media'); this.load(true); } }; //this.scroll.attachSentinels(undefined, 400); - this.selectTab = horizontalMenu(this.tabsMenu, this.tabsContainer, (id, tabContent) => { - if(this.prevTabId === id) return; - - if(this.prevTabId !== -1) { - this.onTransitionStart(); + this.selectTab = horizontalMenu(this.tabsMenu, this.tabsContainer, (id, tabContent, animate) => { + if(this.prevTabId === id && !this.skipScroll) { + this.scrollable.scrollIntoViewNew(this.container, 'start'); + return; } - this.mediaTab.scroll = {scrollTop: this.scrollable.scrollTop, scrollHeight: this.scrollable.scrollHeight}; - const newMediaTab = this.mediaTabs[id]; - this.tabSelected = tabContent.firstElementChild as HTMLDivElement; - - if(newMediaTab.scroll === undefined) { - const rect = this.container.getBoundingClientRect(); - const rect2 = this.container.parentElement.getBoundingClientRect(); - const diff = rect.y - rect2.y; - - if(this.scrollable.scrollTop > diff) { - newMediaTab.scroll = {scrollTop: diff, scrollHeight: 0}; - } + if(this.onChangeTab) { + this.onChangeTab(newMediaTab); } + + const fromMediaTab = this.mediaTab; + this.mediaTab = newMediaTab; - if(newMediaTab.scroll) { - const diff = this.mediaTab.scroll.scrollTop - newMediaTab.scroll.scrollTop; - //console.log('what you gonna do', this.goingHard, diff); - - if(diff/* && diff < 0 */) { - this.tabSelected.style.transform = `translateY(${diff}px)`; - } + if(this.prevTabId !== -1 && animate) { + this.onTransitionStart(); } - this.mediaTab = newMediaTab; - - if(this.onChangeTab) { - this.onChangeTab(this.mediaTab); + if(this.skipScroll) { + this.skipScroll = false; + } else { + const offsetTop = this.container.offsetTop; + let scrollTop = this.scrollable.scrollTop; + if(scrollTop < offsetTop) { + this.scrollable.scrollIntoViewNew(this.container, 'start'); + scrollTop = offsetTop; + } + + fromMediaTab.scroll = {scrollTop: scrollTop, scrollHeight: this.scrollable.scrollHeight}; + + if(newMediaTab.scroll === undefined) { + const rect = this.container.getBoundingClientRect(); + const rect2 = this.container.parentElement.getBoundingClientRect(); + const diff = rect.y - rect2.y; + + if(scrollTop > diff) { + newMediaTab.scroll = {scrollTop: diff, scrollHeight: 0}; + } + } + + if(newMediaTab.scroll) { + const diff = fromMediaTab.scroll.scrollTop - newMediaTab.scroll.scrollTop; + //console.log('what you gonna do', this.goingHard, diff); + + //this.scrollable.scrollTop = scrollTop; + if(diff/* && diff < 0 */) { + /* if(diff > -(fromMediaTab.contentTab.scrollHeight + this.nav.scrollHeight)) { + fromMediaTab.contentTab.style.transform = `translateY(${diff}px)`; + this.scrollable.scrollTop = scrollTop - diff; + } else { */ + newMediaTab.contentTab.style.transform = `translateY(${diff}px)`; + //} + } + } } /* if(this.prevTabId !== -1 && nav.offsetTop) { @@ -236,7 +257,7 @@ export default class AppSearchSuper { /* this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount); this.scroll.setVirtualContainer(this.sharedMediaSelected); */ - if(this.prevTabId !== -1 && !this.tabSelected.childElementCount) { // quick brown fix + if(this.prevTabId !== -1 && !newMediaTab.contentTab.childElementCount) { // quick brown fix //this.contentContainer.classList.remove('loaded'); this.load(true); } @@ -247,7 +268,7 @@ export default class AppSearchSuper { //console.log('what y', this.tabSelected.style.transform); if(this.mediaTab.scroll !== undefined) { - this.tabSelected.style.transform = ''; + this.mediaTab.contentTab.style.transform = ''; this.scrollable.scrollTop = this.mediaTab.scroll.scrollTop; } @@ -1097,6 +1118,7 @@ export default class AppSearchSuper { } let firstMediaTab: SearchSuperMediaTab; + let count = 0; mediaTabs.forEach(mediaTab => { const counter = counters.find(c => c.filter._ === mediaTab.inputFilter); @@ -1107,6 +1129,8 @@ export default class AppSearchSuper { if(counter.count && firstMediaTab === undefined) { firstMediaTab = mediaTab; } + + if(counter.count) ++count; }); const membersTab = this.mediaTabsMap.get('members'); @@ -1120,8 +1144,11 @@ export default class AppSearchSuper { this.container.classList.toggle('hide', !firstMediaTab); this.container.parentElement.classList.toggle('search-empty', !firstMediaTab); if(firstMediaTab) { + this.skipScroll = true; this.selectTab(this.mediaTabs.indexOf(firstMediaTab), false); firstMediaTab.menuTab.classList.add('active'); + + this.navScrollableContainer.classList.toggle('hide', count <= 1); } } diff --git a/src/components/audio.ts b/src/components/audio.ts index 830fe2d7..83d311c0 100644 --- a/src/components/audio.ts +++ b/src/components/audio.ts @@ -217,9 +217,10 @@ function wrapVoiceMessage(audioEl: AudioElement) { e.preventDefault(); if(!audio.paused) { audio.pause(); - scrub(e); - mousedown = true; } + + scrub(e); + mousedown = true; }); progress.addEventListener('mouseup', (e) => { if (mousemove && mousedown) { @@ -437,7 +438,7 @@ export default class AudioElement extends HTMLElement { const getDownloadPromise = () => appDocsManager.downloadDoc(doc); - if(isVoice) { + if(isRealVoice) { if(!preloader) { preloader = new ProgressivePreloader({ cancelable: true diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 5af4e50a..7b18420f 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -168,6 +168,7 @@ export default class ChatBubbles { const message = this.chat.getMessage(mid); if(+bubble.dataset.timestamp >= (message.date + serverTimeManager.serverTimeOffset - 1)) { + this.bubbleGroups.addBubble(bubble, message, false); return; } @@ -1025,6 +1026,7 @@ export default class ChatBubbles { // if scroll down after search if(history.indexOf(historyStorage.maxId) !== -1) { + this.scrollable.loadedAll.bottom = true; return; } @@ -1414,6 +1416,10 @@ export default class ChatBubbles { const samePeer = this.peerId === peerId; + /* if(samePeer && this.chat.setPeerPromise) { + return {cached: true, promise: this.chat.setPeerPromise}; + } */ + const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId); let topMessage = this.chat.type === 'pinned' ? this.appMessagesManager.pinnedMessages[peerId].maxId : historyStorage.maxId ?? 0; const isTarget = lastMsgId !== undefined; @@ -1497,9 +1503,15 @@ export default class ChatBubbles { this.isFirstLoad = true; } + const oldChatInner = this.chatInner; this.cleanup(); this.chatInner = document.createElement('div'); - this.chatInner.classList.add('bubbles-inner'); + if(samePeer) { + this.chatInner.className = oldChatInner.className; + this.chatInner.classList.remove('disable-hover', 'is-scrolling'); + } else { + this.chatInner.classList.add('bubbles-inner'); + } this.lazyLoadQueue.lock(); @@ -1590,6 +1602,8 @@ export default class ChatBubbles { this.scrollable.scrollTop = 99999; } + this.onScroll(); + this.chat.dispatchEvent('setPeer', lastMsgId, !isJump); // warning @@ -1718,7 +1732,7 @@ export default class ChatBubbles { public setBubblePosition(bubble: HTMLElement, message: any, reverse: boolean) { const dateMessage = this.getDateContainerByMessage(message, reverse); - if(this.chat.type === 'scheduled' || this.chat.type === 'pinned') { + if(this.chat.type === 'scheduled' || this.chat.type === 'pinned' || true) { const offset = this.stickyIntersector ? 2 : 1; let children = Array.from(dateMessage.container.children).slice(offset) as HTMLElement[]; let i = 0, foundMidOnSameTimestamp = 0; diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 8ffe83f0..2c45d164 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -206,6 +206,8 @@ export default class Chat extends EventListenerBase<{ if(!samePeer) { rootScope.broadcast('peer_changing', this); this.peerId = peerId; + } else if(this.setPeerPromise) { + return; } //console.time('appImManager setPeer'); diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index 32c9de3e..03ff0ae0 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -157,9 +157,6 @@ export default class ChatInput { this.chatInput.append(this.inputContainer); this.goDownBtn = Button('bubbles-go-down btn-corner btn-circle z-depth-1 hide', {icon: 'arrow_down'}); - this.goDownUnreadBadge = document.createElement('span'); - this.goDownUnreadBadge.classList.add('badge', 'badge-24', 'badge-primary'); - this.goDownBtn.append(this.goDownUnreadBadge); this.inputContainer.append(this.goDownBtn); attachClickEvent(this.goDownBtn, (e) => { @@ -257,6 +254,10 @@ export default class ChatInput { this.inputMessageContainer.classList.add('input-message-container'); if(this.chat.type === 'chat') { + this.goDownUnreadBadge = document.createElement('span'); + this.goDownUnreadBadge.classList.add('badge', 'badge-24', 'badge-primary'); + this.goDownBtn.append(this.goDownUnreadBadge); + this.btnScheduled = ButtonIcon('scheduled', {noRipple: true}); this.btnScheduled.classList.add('btn-scheduled', 'hide'); @@ -376,7 +377,7 @@ export default class ChatInput { this.inputContainer.append(this.btnCancelRecord, this.btnSendContainer); - emoticonsDropdown.attachButtonListener(this.btnToggleEmoticons); + emoticonsDropdown.attachButtonListener(this.btnToggleEmoticons, this.listenerSetter); emoticonsDropdown.events.onOpen.push(this.onEmoticonsOpen); emoticonsDropdown.events.onClose.push(this.onEmoticonsClose); diff --git a/src/components/emoticonsDropdown/index.ts b/src/components/emoticonsDropdown/index.ts index cf1ee207..0edebf8e 100644 --- a/src/components/emoticonsDropdown/index.ts +++ b/src/components/emoticonsDropdown/index.ts @@ -8,7 +8,7 @@ import { isTouchSupported } from "../../helpers/touchSupport"; import appChatsManager from "../../lib/appManagers/appChatsManager"; import appImManager from "../../lib/appManagers/appImManager"; import rootScope from "../../lib/rootScope"; -import { blurActiveElement, whichChild } from "../../helpers/dom"; +import { attachClickEvent, blurActiveElement, whichChild } from "../../helpers/dom"; import animationIntersector from "../animationIntersector"; import { horizontalMenu } from "../horizontalMenu"; import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue"; @@ -24,6 +24,7 @@ import AppGifsTab from "../sidebarRight/tabs/gifs"; import AppStickersTab from "../sidebarRight/tabs/stickers"; import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpTag from "../../helpers/dom/findUpTag"; +import ListenerSetter from "../../helpers/listenerSetter"; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; @@ -32,7 +33,9 @@ export interface EmoticonsTab { onCloseAfterTimeout?: () => void } -const test = false; +const KEEP_OPEN = false; +const TOGGLE_TIMEOUT = 200; +const ANIMATION_DURATION = 200; export class EmoticonsDropdown { public static lazyLoadQueue = new LazyLoadQueue(); @@ -72,36 +75,36 @@ export class EmoticonsDropdown { this.element = document.getElementById('emoji-dropdown') as HTMLDivElement; } - public attachButtonListener(button: HTMLElement) { + public attachButtonListener(button: HTMLElement, listenerSetter: ListenerSetter) { let firstTime = true; if(isTouchSupported) { - button.addEventListener('click', () => { + attachClickEvent(button, () => { if(firstTime) { firstTime = false; this.toggle(true); } else { this.toggle(); } - }); + }, {listenerSetter}); } else { - button.onmouseover = (e) => { + listenerSetter.add(button, 'mouseover', (e) => { //console.log('onmouseover button'); - clearTimeout(this.displayTimeout); - //this.displayTimeout = setTimeout(() => { - if(firstTime) { - button.onmouseout = this.onMouseOut; - - firstTime = false; - } + if(firstTime) { + listenerSetter.add(button, 'mouseout', this.onMouseOut); + firstTime = false; + } + clearTimeout(this.displayTimeout); + this.displayTimeout = window.setTimeout(() => { this.toggle(true); - //}, 0/* 200 */); - }; + }, TOGGLE_TIMEOUT); + }); } } private onMouseOut = (e: MouseEvent) => { - if(test) return; + if(KEEP_OPEN) return; + clearTimeout(this.displayTimeout); if(!this.element.classList.contains('active')) return; const toElement = (e as any).toElement as Element; @@ -109,10 +112,9 @@ export class EmoticonsDropdown { return; } - clearTimeout(this.displayTimeout); this.displayTimeout = window.setTimeout(() => { this.toggle(false); - }, 200); + }, TOGGLE_TIMEOUT); }; private init() { @@ -265,7 +267,7 @@ export class EmoticonsDropdown { this.container.classList.remove('disable-hover'); this.events.onOpenAfter.forEach(cb => cb()); - }, isTouchSupported ? 0 : 200); + }, isTouchSupported ? 0 : ANIMATION_DURATION); // ! can't use together with resizeObserver /* if(isTouchSupported) { @@ -302,7 +304,7 @@ export class EmoticonsDropdown { this.container.classList.remove('disable-hover'); this.events.onCloseAfter.forEach(cb => cb()); - }, isTouchSupported ? 0 : 200); + }, isTouchSupported ? 0 : ANIMATION_DURATION); /* if(isTouchSupported) { const scrollHeight = this.container.scrollHeight; diff --git a/src/components/emoticonsDropdown/tabs/stickers.ts b/src/components/emoticonsDropdown/tabs/stickers.ts index e243aaaa..04746e31 100644 --- a/src/components/emoticonsDropdown/tabs/stickers.ts +++ b/src/components/emoticonsDropdown/tabs/stickers.ts @@ -209,37 +209,44 @@ export default class StickersTab implements EmoticonsTab { //console.log('got stickerSet', stickerSet, li); if(stickerSet.set.thumbs?.length) { - const downloadOptions = appStickersManager.getStickerSetThumbDownloadOptions(stickerSet.set); - const promise = appDownloadManager.download(downloadOptions); - - if(stickerSet.set.pFlags.animated) { - promise - .then(readBlobAsText) - //.then(JSON.parse) - .then(json => { - lottieLoader.loadAnimationWorker({ - container: button, - loop: true, - autoplay: false, - animationData: json, - width: 32, - height: 32, - needUpscale: true - }, EMOTICONSSTICKERGROUP); - }); - } else { - const image = new Image(); - promise.then(blob => { - renderImageFromUrl(image, URL.createObjectURL(blob), () => { - button.append(image); - }); - }); - } + EmoticonsDropdown.lazyLoadQueue.push({ + div: button, + load: () => { + const downloadOptions = appStickersManager.getStickerSetThumbDownloadOptions(stickerSet.set); + const promise = appDownloadManager.download(downloadOptions); + + if(stickerSet.set.pFlags.animated) { + return promise + .then(readBlobAsText) + //.then(JSON.parse) + .then(json => { + lottieLoader.loadAnimationWorker({ + container: button, + loop: true, + autoplay: false, + animationData: json, + width: 32, + height: 32, + needUpscale: true + }, EMOTICONSSTICKERGROUP); + }); + } else { + const image = new Image(); + + return promise.then(blob => { + renderImageFromUrl(image, URL.createObjectURL(blob), () => { + button.append(image); + }); + }); + } + } + }); } else if(stickerSet.documents[0]._ !== 'documentEmpty') { // as thumb will be used first sticker wrapSticker({ doc: stickerSet.documents[0], div: button as any, - group: EMOTICONSSTICKERGROUP + group: EMOTICONSSTICKERGROUP, + lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue }); // kostil } } diff --git a/src/components/horizontalMenu.ts b/src/components/horizontalMenu.ts index 848820e1..20c811cc 100644 --- a/src/components/horizontalMenu.ts +++ b/src/components/horizontalMenu.ts @@ -12,7 +12,7 @@ import { fastRaf } from "../helpers/schedulers"; import { FocusDirection } from "../helpers/fastSmoothScroll"; import findUpAsChild from "../helpers/dom/findUpAsChild"; -export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 250, scrollableX?: ScrollableX) { +export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement, animate: boolean) => void | boolean, onTransitionEnd?: () => void, transitionTime = 250, scrollableX?: ScrollableX) { const selectTab = TransitionSlider(content, tabs || content.dataset.animation === 'tabs' ? 'tabs' : 'navigation', transitionTime, onTransitionEnd); if(tabs) { @@ -29,7 +29,12 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick? const selectTarget = (target: HTMLElement, id: number, animate = true) => { const tabContent = content.children[id] as HTMLDivElement; - if(onClick) onClick(id, tabContent); + if(onClick) { + const canChange = onClick(id, tabContent, animate); + if(canChange !== undefined && !canChange) { + return; + } + } if(scrollableX) { scrollableX.scrollIntoViewNew(target.parentElement.children[id] as HTMLElement, 'center', undefined, undefined, animate ? undefined : FocusDirection.Static, transitionTime, 'x'); diff --git a/src/components/inputField.ts b/src/components/inputField.ts index bd5696aa..c344263a 100644 --- a/src/components/inputField.ts +++ b/src/components/inputField.ts @@ -159,6 +159,12 @@ class InputField { } } + if(label || placeholder) { + const border = document.createElement('div'); + border.classList.add('input-field-border'); + this.container.append(border); + } + if(label) { this.label = document.createElement('label'); this.setLabel(); diff --git a/src/components/inputSearch.ts b/src/components/inputSearch.ts index da38037c..e405bb04 100644 --- a/src/components/inputSearch.ts +++ b/src/components/inputSearch.ts @@ -43,7 +43,7 @@ export default class InputSearch { this.input.addEventListener('input', this.onInput); this.clearBtn.addEventListener('click', this.onClearClick); - this.container.append(this.input, searchIcon, this.clearBtn); + this.container.append(searchIcon, this.clearBtn); } onInput = () => { diff --git a/src/components/scrollable.ts b/src/components/scrollable.ts index 921079ba..4b3a3bcf 100644 --- a/src/components/scrollable.ts +++ b/src/components/scrollable.ts @@ -250,8 +250,8 @@ export class ScrollableX extends ScrollableBase { if(!isTouchSupported) { const scrollHorizontally = (e: any) => { - if(!e.deltaX) { - this.container!.scrollLeft += e.deltaY / 4; + if(!e.deltaX && this.container.scrollWidth > this.container.clientWidth) { + this.container.scrollLeft += e.deltaY / 4; cancelEvent(e); } }; diff --git a/src/components/sidebarRight/tabs/sharedMedia.ts b/src/components/sidebarRight/tabs/sharedMedia.ts index f856a1ac..4a317412 100644 --- a/src/components/sidebarRight/tabs/sharedMedia.ts +++ b/src/components/sidebarRight/tabs/sharedMedia.ts @@ -46,10 +46,10 @@ import Scrollable from "../../scrollable"; import { isTouchSupported } from "../../../helpers/touchSupport"; let setText = (text: string, row: Row) => { - fastRaf(() => { + //fastRaf(() => { row.title.innerHTML = text; row.container.style.display = ''; - }); + //}); }; type ListLoaderResult = {count: number, items: any[]}; diff --git a/src/config/app.ts b/src/config/app.ts index f7b6356e..75a1b40a 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -12,8 +12,8 @@ const App = { id: 1025907, hash: '452b0359b988148995f22ff0f4229750', - version: '0.4.1', - langPackVersion: '0.1.3', + version: '0.4.2', + langPackVersion: '0.1.4', langPack: 'macos', langPackCode: 'en', domains: [] as string[], diff --git a/src/lang.ts b/src/lang.ts index b2e77538..c16bf586 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -91,6 +91,15 @@ const lang = { "Profile": "Profile", "Saved": "Saved", "ReportBug": "Report Bug", + "Notifications.Count": { + "one_value": "%d notification", + "other_value": "%d notifications", + }, + "Notifications.Forwarded": { + "one_value": "Forwarded %d message", + "other_value": "Forwarded %d messages" + }, + "Notifications.New": "New notification", // * android "ActionCreateChannel": "Channel created", diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index da2ed556..03d556b6 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -477,7 +477,7 @@ export class AppChatsManager { apiUpdatesManager.processUpdateMessage(updates); const channelId = updates.chats[0].id; - rootScope.broadcast('history_focus', -channelId); + rootScope.broadcast('history_focus', {peerId: -channelId}); return channelId; }); @@ -503,7 +503,7 @@ export class AppChatsManager { apiUpdatesManager.processUpdateMessage(updates); const chatId = (updates as any as Updates.updates).chats[0].id; - rootScope.broadcast('history_focus', -chatId); + rootScope.broadcast('history_focus', {peerId: -chatId}); return chatId; }); diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index f33fb691..e4a6ab0c 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -233,6 +233,7 @@ export class AppDialogsManager { }; this.setListClickListener(archivedChatList, null, true); + //this.setListClickListener(archivedChatList, null, true); // * to test peer changing this.chatsPreloader = putPreloader(null, true); diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 0203441c..3795ed34 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -169,8 +169,9 @@ export class AppImManager { } }); - rootScope.on('history_focus', (peerId) => { - this.setInnerPeer(peerId); + rootScope.on('history_focus', (e) => { + const {peerId, mid} = e; + this.setInnerPeer(peerId, mid); }); rootScope.on('peer_changing', (chat) => { diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index b00966ce..40004fd8 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -2953,7 +2953,11 @@ export class AppMessagesManager { } } - public canMessageBeEdited(message: any, kind: 'text' | 'poll') { + private canMessageBeEdited(message: any, kind: 'text' | 'poll') { + if(message.pFlags.is_outgoing) { + return false; + } + const goodMedias = [ 'messageMediaPhoto', 'messageMediaDocument', @@ -2992,7 +2996,7 @@ export class AppMessagesManager { return true; } - if((message.date < tsNow(true) - (2 * 86400) && message.media?._ !== 'messageMediaPoll') || !message.pFlags.out) { + if((message.date < (tsNow(true) - (2 * 86400)) && message.media?._ !== 'messageMediaPoll') || !message.pFlags.out) { return false; } @@ -3005,7 +3009,7 @@ export class AppMessagesManager { || message.fromId === rootScope.myId || appChatsManager.getChat(message.peerId)._ === 'chat' || appChatsManager.hasRights(message.peerId, 'delete_messages') - ); + ) && !message.pFlags.is_outgoing; } public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) { @@ -4919,60 +4923,31 @@ export class AppMessagesManager { peerTypeNotifySettings: PeerNotifySettings }> = {}) { const peerId = this.getMessagePeer(message); - let peerString: string; const notification: NotifyOptions = {}; - var notificationMessage: string, - notificationPhoto: any; - - const _ = (str: string) => str; + const peerString = appPeersManager.getPeerString(peerId); + let notificationMessage: string; - const localSettings = appNotificationsManager.getLocalSettings(); if(options.peerTypeNotifySettings.show_previews) { if(message._ === 'message' && message.fwd_from && options.fwdCount) { - notificationMessage = 'Forwarded ' + options.fwdCount + ' messages';//fwdMessagesPluralize(options.fwd_count); + notificationMessage = I18n.format('Notifications.Forwarded', true, [options.fwdCount]); } else { notificationMessage = this.wrapMessageForReply(message, undefined, undefined, true); } } else { - notificationMessage = 'New notification'; + notificationMessage = I18n.format('Notifications.New', true); } - if(peerId > 0) { - const fromUser = appUsersManager.getUser(message.fromId); - const fromPhoto = appUsersManager.getUserPhoto(message.fromId); - - notification.title = (fromUser.first_name || '') + - (fromUser.first_name && fromUser.last_name ? ' ' : '') + - (fromUser.last_name || ''); - if(!notification.title) { - notification.title = fromUser.phone || _('conversation_unknown_user_raw'); - } - - notificationPhoto = fromPhoto; - - peerString = appUsersManager.getUserString(peerId); - } else { - notification.title = appChatsManager.getChat(-peerId).title || _('conversation_unknown_chat_raw'); - - if(message.fromId) { - var fromUser = appUsersManager.getUser(message.fromId); - notification.title = (fromUser.first_name || fromUser.last_name || _('conversation_unknown_user_raw')) + + notification.title = appPeersManager.getPeerTitle(peerId, true); + if(peerId < 0 && message.fromId !== message.peerId) { + notification.title = appPeersManager.getPeerTitle(message.fromId, true) + ' @ ' + notification.title; - } - - notificationPhoto = appChatsManager.getChatPhoto(-peerId); - - peerString = appChatsManager.getChatString(-peerId); } notification.title = RichTextProcessor.wrapPlainText(notification.title); notification.onclick = () => { - /* rootScope.broadcast('history_focus', { - peerString: peerString, - messageID: message.flags & 16 ? message.mid : 0 - }); */ + rootScope.broadcast('history_focus', {peerId, mid: message.mid}); }; notification.message = notificationMessage; @@ -4980,16 +4955,17 @@ export class AppMessagesManager { notification.tag = peerString; notification.silent = true;//message.pFlags.silent || false; - /* if(notificationPhoto.location && !notificationPhoto.location.empty) { - apiManager.downloadSmallFile(notificationPhoto.location, notificationPhoto.size).then(function (blob) { - if (message.pFlags.unread) { - notification.image = blob - NotificationsManager.notify(notification) + const peerPhoto = appPeersManager.getPeerPhoto(peerId); + if(peerPhoto) { + appProfileManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => { + if(message.pFlags.unread) { + notification.image = url; + appNotificationsManager.notify(notification); } - }) - } else { */ + }); + } else { appNotificationsManager.notify(notification); - //} + } } public getScheduledMessagesStorage(peerId: number) { diff --git a/src/lib/appManagers/appNotificationsManager.ts b/src/lib/appManagers/appNotificationsManager.ts index 06d6a686..7350519a 100644 --- a/src/lib/appManagers/appNotificationsManager.ts +++ b/src/lib/appManagers/appNotificationsManager.ts @@ -17,6 +17,7 @@ import { copy, deepEqual } from "../../helpers/object"; import { convertInputKeyToKey } from "../../helpers/string"; import { isMobile } from "../../helpers/userAgent"; import { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySettings, Update } from "../../layer"; +import I18n from "../langPack"; import apiManager from "../mtproto/mtprotoworker"; import rootScope from "../rootScope"; import sessionStorage from "../sessionStorage"; @@ -55,10 +56,9 @@ export class AppNotificationsManager { notifyChats: null as ImSadAboutIt, notifyBroadcasts: null as ImSadAboutIt }; - private exceptions: {[peerId: string]: PeerNotifySettings} = {}; + //private exceptions: {[peerId: string]: PeerNotifySettings} = {}; private notifyContactsSignUp: Promise; private faviconEl: HTMLLinkElement = document.head.querySelector('link[rel="icon"]'); - private langNotificationsPluralize = 'notifications';//_.pluralize('page_title_pluralize_notifications'); private titleBackup = document.title; private titleChanged = false; @@ -221,7 +221,7 @@ export class AppNotificationsManager { resetTitle(); } else { this.titleChanged = true; - document.title = this.notificationsCount + ' ' + this.langNotificationsPluralize; + document.title = I18n.format('Notifications.Count', true, [this.notificationsCount]); //this.setFavicon('assets/img/favicon_unread.ico'); // fetch('assets/img/favicon.ico') @@ -499,8 +499,8 @@ export class AppNotificationsManager { data.image = FileManager.getUrl(data.image, 'image/jpeg') } } - else if (!data.image) */ { - data.image = 'assets/img/logo.svg'; + else */ if(!data.image) { + data.image = 'assets/img/logo_filled_rounded.png'; } // console.log('notify image', data.image) diff --git a/src/lib/appManagers/appProfileManager.ts b/src/lib/appManagers/appProfileManager.ts index 321f7e95..e8ec8adc 100644 --- a/src/lib/appManagers/appProfileManager.ts +++ b/src/lib/appManagers/appProfileManager.ts @@ -37,7 +37,7 @@ export class AppProfileManager { private savedAvatarURLs: { [peerId: number]: { - [size in PeerPhotoSize]?: string | Promise + [size in PeerPhotoSize]?: string | Promise } } = {}; @@ -455,11 +455,11 @@ export class AppProfileManager { } } - public putAvatar(div: HTMLElement, peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize, img = new Image()) { + public loadAvatar(peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize) { const inputPeer = appPeersManager.getInputPeerById(peerId); - let needFadeIn = true; - let getAvatarPromise: Promise; + let cached = false; + let getAvatarPromise: Promise; let saved = this.savedAvatarURLs[peerId]; if(!saved || !saved[size]) { if(!saved) { @@ -489,7 +489,7 @@ export class AppProfileManager { const promise = appDownloadManager.download(downloadOptions); getAvatarPromise = saved[size] = promise.then(blob => { - saved[size] = URL.createObjectURL(blob); + return saved[size] = URL.createObjectURL(blob); /* if(str) { console.log(str, Date.now() / 1000, Date.now() - time); @@ -498,12 +498,18 @@ export class AppProfileManager { } else if(typeof(saved[size]) !== 'string') { getAvatarPromise = saved[size] as Promise; } else { - getAvatarPromise = Promise.resolve(); - needFadeIn = false; + getAvatarPromise = Promise.resolve(saved[size]); + cached = true; } + return {cached, loadPromise: getAvatarPromise}; + } + + public putAvatar(div: HTMLElement, peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize, img = new Image()) { + const {cached, loadPromise} = this.loadAvatar(peerId, photo, size); + let callback: () => void; - if(!needFadeIn) { + if(cached) { // смотри в misc.ts: renderImageFromUrl callback = () => { replaceContent(div, img); @@ -532,16 +538,16 @@ export class AppProfileManager { }; } - const loadPromise = getAvatarPromise.then(() => { + const renderPromise = loadPromise.then((url) => { return new Promise((resolve) => { - renderImageFromUrl(img, saved[size] as string, () => { + renderImageFromUrl(img, url, () => { callback(); resolve(); }/* , false */); }); }); - return {cached: !needFadeIn, loadPromise}; + return {cached, loadPromise: renderPromise}; } // peerId === peerId || title diff --git a/src/lib/mtproto/networker.ts b/src/lib/mtproto/networker.ts index 7539f426..155303c9 100644 --- a/src/lib/mtproto/networker.ts +++ b/src/lib/mtproto/networker.ts @@ -74,6 +74,7 @@ export type MTMessage = InvokeApiOptions & MTMessageOptions & { }; const CONNECTION_TIMEOUT = 5000; +let invokeAfterMsgConstructor: number; export default class MTPNetworker { private authKeyUint8: Uint8Array; @@ -320,15 +321,21 @@ export default class MTPNetworker { } if(options.afterMessageId) { - const invokeAfterMsg = Schema.API.methods.find(m => m.method === 'invokeAfterMsg'); - if(!invokeAfterMsg) throw new Error('no invokeAfterMsg!'); - - if(this.debug) { - this.log('Api call options.afterMessageId!'); + if(invokeAfterMsgConstructor === undefined) { + const m = Schema.API.methods.find(m => m.method === 'invokeAfterMsg'); + invokeAfterMsgConstructor = m ? +m.id >>> 0 : 0; + } + + if(invokeAfterMsgConstructor) { + //if(this.debug) { + //this.log('Api call options.afterMessageId!'); + //} + + serializer.storeInt(invokeAfterMsgConstructor, 'invokeAfterMsg'); + serializer.storeLong(options.afterMessageId, 'msg_id'); + } else { + this.log.error('no invokeAfterMsg!'); } - - serializer.storeInt(+invokeAfterMsg.id >>> 0, 'invokeAfterMsg'); - serializer.storeLong(options.afterMessageId, 'msg_id'); } options.resultType = serializer.storeMethod(method, params); diff --git a/src/lib/mtproto/serverTimeManager.ts b/src/lib/mtproto/serverTimeManager.ts index 2fa918b1..8f7476eb 100644 --- a/src/lib/mtproto/serverTimeManager.ts +++ b/src/lib/mtproto/serverTimeManager.ts @@ -9,6 +9,7 @@ * https://github.com/zhukov/webogram/blob/master/LICENSE */ +import { MOUNT_CLASS_TO } from '../../config/debug'; import { tsNow } from '../../helpers/date'; import sessionStorage from '../sessionStorage'; @@ -37,4 +38,6 @@ export class ServerTimeManager { } } -export default new ServerTimeManager(); +const serverTimeManager = new ServerTimeManager(); +MOUNT_CLASS_TO && (MOUNT_CLASS_TO.serverTimeManager = serverTimeManager); +export default serverTimeManager; diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 78a35da6..560d5676 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -51,7 +51,7 @@ export type BroadcastEvents = { 'history_delete': {peerId: number, msgs: {[mid: number]: true}}, 'history_forbidden': number, 'history_reload': number, - 'history_focus': number, + 'history_focus': {peerId: number, mid?: number}, //'history_request': void, 'message_edit': {storage: MessagesStorage, peerId: number, mid: number}, diff --git a/src/pages/pageAuthCode.ts b/src/pages/pageAuthCode.ts index 5d130550..ccc1e39a 100644 --- a/src/pages/pageAuthCode.ts +++ b/src/pages/pageAuthCode.ts @@ -17,6 +17,7 @@ import TrackingMonkey from '../components/monkeys/tracking'; import CodeInputField from '../components/codeInputField'; import { replaceContent } from '../helpers/dom'; import { i18n, LangPackKey } from '../lib/langPack'; +import { randomLong } from '../helpers/random'; let authCode: AuthSentCode.authSentCode = null; @@ -29,7 +30,7 @@ let onFirstMount = (): Promise => { const codeInputField = new CodeInputField({ label: 'Code', - name: 'code', + name: randomLong(), length: CODELENGTH, onFill: (code) => { submitCode('' + code); diff --git a/src/pages/pageSignIn.ts b/src/pages/pageSignIn.ts index d6f3ad58..417beb23 100644 --- a/src/pages/pageSignIn.ts +++ b/src/pages/pageSignIn.ts @@ -25,6 +25,8 @@ import { LangPackString } from "../layer"; import lottieLoader from "../lib/lottieLoader"; import { ripple } from "../components/ripple"; import findUpTag from "../helpers/dom/findUpTag"; +import findUpClassName from "../helpers/dom/findUpClassName"; +import { randomLong } from "../helpers/random"; type Country = _Country & { li?: HTMLLIElement[] @@ -56,14 +58,14 @@ let onFirstMount = () => { const countryInputField = new InputField({ label: 'Login.CountrySelectorLabel', - name: 'countryCode', + name: randomLong(), plainText: true }); countryInputField.container.classList.add('input-select'); const countryInput = countryInputField.input as HTMLInputElement; - countryInput.autocomplete = 'rrRandomRR'; + countryInput.autocomplete = randomLong(); const selectWrapper = document.createElement('div'); selectWrapper.classList.add('select-wrapper', 'z-depth-3', 'hide'); @@ -77,38 +79,25 @@ let onFirstMount = () => { const scroll = new Scrollable(selectWrapper); - let initedSelect = false; - let initSelect = () => { initSelect = null; countries.forEach((c) => { - initedSelect = true; - - /* let unified = unifiedCountryCodeEmoji(c.code); - let emoji = unified.split('-').reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), ''); */ - //let emoji = countryCodeEmoji(c.code); - let emoji = c.emoji; + const emoji = c.emoji; - let liArr: Array = []; + const liArr: Array = []; c.phoneCode.split(' and ').forEach((phoneCode: string) => { - let li = document.createElement('li'); - var spanEmoji = document.createElement('span'); - /* spanEmoji.innerHTML = countryCodeEmoji(c.code); */ - //spanEmoji.classList.add('emoji-outer', 'emoji-sizer'); - //spanEmoji.innerHTML = ``; - - + const li = document.createElement('li'); + const spanEmoji = document.createElement('span'); - let kek = RichTextProcessor.wrapRichText(emoji); - //console.log(c.name, emoji, kek, spanEmoji.innerHTML); + const kek = RichTextProcessor.wrapRichText(emoji); li.appendChild(spanEmoji); spanEmoji.outerHTML = kek; li.append(c.name); - var span = document.createElement('span'); + const span = document.createElement('span'); span.classList.add('phone-code'); span.innerText = '+' + phoneCode; li.appendChild(span); @@ -120,23 +109,32 @@ let onFirstMount = () => { c.li = liArr; }); - selectList.addEventListener('mousedown', function(e) { + selectList.addEventListener('mousedown', (e) => { + if(e.button !== 0) { // other buttons but left shall not pass + return; + } + let target = e.target as HTMLElement; if(target.tagName !== 'LI') target = findUpTag(target, 'LI'); - let countryName = target.childNodes[1].textContent;//target.innerText.split('\n').shift(); - let phoneCode = target.querySelector('.phone-code').innerText; - - countryInput.value = countryName; - lastCountrySelected = countries.find(c => c.name === countryName); - - telEl.value = lastValue = phoneCode; - setTimeout(() => telEl.focus(), 0); + selectCountryByTarget(target); //console.log('clicked', e, countryName, phoneCode); }); countryInputField.container.appendChild(selectWrapper); }; + + const selectCountryByTarget = (target: HTMLElement) => { + const countryName = target.childNodes[1].textContent;//target.innerText.split('\n').shift(); + const phoneCode = target.querySelector('.phone-code').innerText; + + countryInput.value = countryName; + lastCountrySelected = countries.find(c => c.name === countryName); + + telEl.value = lastValue = phoneCode; + hidePicker(); + setTimeout(() => telEl.focus(), 0); + }; initSelect(); @@ -152,6 +150,7 @@ let onFirstMount = () => { } clearTimeout(hideTimeout); + hideTimeout = undefined; selectWrapper.classList.remove('hide'); void selectWrapper.offsetWidth; // reflow @@ -171,9 +170,9 @@ let onFirstMount = () => { let mouseDownHandlerAttached = false; const onMouseDown = (e: MouseEvent) => { - /* if(findUpClassName(e.target, 'input-select')) { + if(findUpClassName(e.target, 'input-select')) { return; - } */ + } if(e.target === countryInput) { return; } @@ -184,9 +183,11 @@ let onFirstMount = () => { }; const hidePicker = () => { + if(hideTimeout !== undefined) return; selectWrapper.classList.remove('active'); hideTimeout = window.setTimeout(() => { selectWrapper.classList.add('hide'); + hideTimeout = undefined; }, 200); }; /* false && countryInput.addEventListener('blur', function(this: typeof countryInput, e) { @@ -221,6 +222,8 @@ let onFirstMount = () => { countries.forEach((c) => { c.li.forEach(li => li.style.display = ''); }); + } else if(matches.length === 1 && e.key === 'Enter') { + selectCountryByTarget(matches[0].li[0]); } }); diff --git a/src/scss/partials/_audio.scss b/src/scss/partials/_audio.scss index c0d59950..a4b0d19c 100644 --- a/src/scss/partials/_audio.scss +++ b/src/scss/partials/_audio.scss @@ -359,6 +359,10 @@ //overflow: visible!important; opacity: .3; + @include hover() { + opacity: 1; + } + &.active, .audio.is-unread:not(.is-out) & { opacity: 1; } diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index 017b6ba2..6db9466a 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -112,7 +112,7 @@ &.active { visibility: visible; opacity: 1; - transform: scale(1); + transform: scale3d(1, 1, 1); // * scale3d (NOT scale) will fix jumping text } &:not(.active) { @@ -178,7 +178,7 @@ @include hover-background-effect(); &.danger { - @include hover-background-effect(red); + @include hover-background-effect(danger); } &:before { diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index efb3fedd..a7ec0902 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -1041,12 +1041,6 @@ $chat-helper-size: 39px; } //} } - - .preloader-container { - .preloader-circular { - background-color: rgba(0, 0, 0, .3); - } - } .search-group.search-group-messages { padding: 0.25rem 0 .5rem; @@ -1139,7 +1133,7 @@ $chat-helper-size: 39px; position: absolute; background-color: var(--surface-color); border-radius: 50%; - color: $placeholder-color; + color: var(--secondary-text-color); font-size: 1.5rem; display: flex; align-items: center; @@ -1171,6 +1165,10 @@ $chat-helper-size: 39px; position: absolute; top: -.25rem; right: -.25rem; + + &.badge-primary { + background-color: var(--chatlist-status-color); + } @include respond-to(handhelds) { top: -.75rem; diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 4e10da87..241ecdbb 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -1279,8 +1279,8 @@ $bubble-margin: .25rem; visibility: visible; } - .tgico-pinnedchat:before { - font-weight: bold; + .tgico-pinnedchat { + margin-right: .125rem; } } diff --git a/src/scss/partials/_input.scss b/src/scss/partials/_input.scss index a6226278..533ebd7e 100644 --- a/src/scss/partials/_input.scss +++ b/src/scss/partials/_input.scss @@ -11,6 +11,7 @@ .input-field { --height: 54px; + --border-radius: #{$border-radius-medium}; position: relative; @include respond-to(handhelds) { @@ -62,27 +63,39 @@ transition: .2s transform, .2s padding, .1s opacity, font-weight 0s .1s; } } + + &-border { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + border: 2px solid var(--primary-color); + opacity: 0; + border-radius: var(--border-radius); + pointer-events: none; + + @include animation-level(2) { + transition: opacity .2s; + } + } &-input { - --padding: 1.0625rem; + --padding: 1rem; --padding-horizontal: 1rem; --border-width: 1px; - --border-width-top: 2px; border: var(--border-width) solid var(--input-search-border-color); - border-radius: $border-radius-medium; + border-radius: var(--border-radius); background-color: transparent; //padding: 0 1rem; - padding: calc(var(--padding) - var(--border-width-top)) calc(var(--padding-horizontal) - var(--border-width)); + padding: calc(var(--padding) - var(--border-width)); box-sizing: border-box; width: 100%; min-height: var(--height); transition: 0s border-color; position: relative; z-index: 1; - line-height: 1.3125; - //line-height: calc(54px - var(--border-width)); - /* overflow: hidden; - white-space: nowrap; */ + line-height: var(--line-height); @include respond-to(handhelds) { --padding: .9375rem; @@ -116,8 +129,6 @@ } */ &:focus { - --border-width: 2px; - --border-width-top: 3px; border-color: var(--primary-color); //padding: 0 calc(1rem - 1px); } @@ -133,6 +144,10 @@ & ~ label { color: var(--danger-color) !important; } + + & ~ .input-field-border { + border-color: var(--danger-color) !important; + } } &.valid { @@ -141,6 +156,10 @@ & ~ label { color: #26962F !important; } + + & ~ .input-field-border { + border-color: #26962F !important; + } } /* &.error, &.valid { @@ -158,11 +177,15 @@ font-weight: 500; } - // * valid for plain text, empty for contenteditable + &:focus ~ .input-field-border { + opacity: 1; + } + + /* // * valid for plain text, empty for contenteditable &:valid ~ label, &:not(:empty):focus ~ label { transition-delay: 0s, 0s, 0s, 0s; - } + } */ &:focus ~ label, &:valid ~ label, @@ -252,6 +275,7 @@ input:focus, button:focus { margin-left: .4375rem; margin-right: .4375rem; overflow: hidden; + --border-radius: 22px; @include respond-to(handhelds) { margin-left: 1rem; @@ -265,7 +289,6 @@ input:focus, button:focus { min-height: var(--height) !important; max-height: var(--height) !important; //line-height: calc(var(--height) + 2px - var(--border-width) * 2); - border-radius: 22px; border-color: var(--input-search-border-color); line-height: var(--height); @@ -281,7 +304,6 @@ input:focus, button:focus { } &:focus { - --border-width: 2px; background-color: transparent; border-color: var(--primary-color); diff --git a/src/scss/partials/_preloader.scss b/src/scss/partials/_preloader.scss index d93c6078..7622d039 100644 --- a/src/scss/partials/_preloader.scss +++ b/src/scss/partials/_preloader.scss @@ -70,7 +70,7 @@ $transition: .2s ease-in-out; .preloader-circular { animation: none; - background-color: rgba(0, 0, 0, .7); + background-color: rgba(0, 0, 0, .3); border-radius: 50%; width: 100%; height: 100%; diff --git a/src/scss/partials/_rightSidebar.scss b/src/scss/partials/_rightSidebar.scss index ccfc3d28..4de4b100 100644 --- a/src/scss/partials/_rightSidebar.scss +++ b/src/scss/partials/_rightSidebar.scss @@ -173,6 +173,7 @@ justify-content: center; font-size: 1rem; line-height: var(--line-height); + border-radius: 0; } .menu-horizontal-div i { @@ -287,6 +288,10 @@ > div:first-child { transform: translateY(0); + + // * fix saving scroll on tab switching, when FROM tab has height < 100vh, and another is scrolled less than the FROM tab's height + // * adding 1 extra pixel for scroll + min-height: calc(100vh - 111px); } } diff --git a/src/scss/partials/popups/_createPoll.scss b/src/scss/partials/popups/_createPoll.scss index e312eb03..ccd7818a 100644 --- a/src/scss/partials/popups/_createPoll.scss +++ b/src/scss/partials/popups/_createPoll.scss @@ -45,11 +45,16 @@ .btn-icon { position: absolute; - right: .5rem; - top: .5rem; + right: .4375rem; + top: .4375rem; z-index: 1; opacity: 1; transition: opacity .2s ease; + + @include respond-to(handhelds) { + right: .3125rem; + top: .3125rem; + } } /* &:last-child:not(:nth-child(10)) { .btn-icon { @@ -65,7 +70,7 @@ } .poll-create-questions { - padding: 0px 1.25rem 2.03125rem; + padding: 0px 1.25rem 1.5rem; .input-field-input { padding-right: 3.25rem; @@ -90,4 +95,4 @@ top: 54px; right: 20px; } -} \ No newline at end of file +}