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
This commit is contained in:
Eduard Kuzmenko 2021-04-18 15:55:56 +04:00
parent baa35fd35b
commit 198eea41ee
34 changed files with 347 additions and 241 deletions

View File

@ -42,9 +42,9 @@ Source maps are included in production build for your convenience.
Should be applied like that: http://localhost:8080/?test=1 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 ### Licensing

View File

@ -70,10 +70,10 @@ export default class AppSearchSuper {
public tabs: {[t in SearchSuperType]: HTMLDivElement} = {} as any; public tabs: {[t in SearchSuperType]: HTMLDivElement} = {} as any;
public mediaTab: SearchSuperMediaTab; public mediaTab: SearchSuperMediaTab;
public tabSelected: HTMLElement;
public container: HTMLElement; public container: HTMLElement;
public nav: HTMLElement; public nav: HTMLElement;
private navScrollableContainer: HTMLDivElement;
private tabsContainer: HTMLElement; private tabsContainer: HTMLElement;
private tabsMenu: HTMLElement; private tabsMenu: HTMLElement;
private prevTabId = -1; private prevTabId = -1;
@ -112,6 +112,8 @@ export default class AppSearchSuper {
private membersList: SortedUserList; private membersList: SortedUserList;
private skipScroll: boolean;
// * arguments // * arguments
public mediaTabs: SearchSuperMediaTab[]; public mediaTabs: SearchSuperMediaTab[];
public scrollable: Scrollable; public scrollable: Scrollable;
@ -128,7 +130,7 @@ export default class AppSearchSuper {
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add('search-super'); 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'); navScrollableContainer.classList.add('search-super-tabs-scrollable', 'menu-horizontal-scrollable', 'sticky');
const navScrollable = new ScrollableX(navScrollableContainer); const navScrollable = new ScrollableX(navScrollableContainer);
@ -185,48 +187,67 @@ export default class AppSearchSuper {
this.searchGroupMedia = new SearchGroup('', 'messages', true); this.searchGroupMedia = new SearchGroup('', 'messages', true);
this.scrollable.onScrolledBottom = () => { 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.log('onScrolledBottom will load media');
this.load(true); this.load(true);
} }
}; };
//this.scroll.attachSentinels(undefined, 400); //this.scroll.attachSentinels(undefined, 400);
this.selectTab = horizontalMenu(this.tabsMenu, this.tabsContainer, (id, tabContent) => { this.selectTab = horizontalMenu(this.tabsMenu, this.tabsContainer, (id, tabContent, animate) => {
if(this.prevTabId === id) return; if(this.prevTabId === id && !this.skipScroll) {
this.scrollable.scrollIntoViewNew(this.container, 'start');
if(this.prevTabId !== -1) { return;
this.onTransitionStart();
} }
this.mediaTab.scroll = {scrollTop: this.scrollable.scrollTop, scrollHeight: this.scrollable.scrollHeight};
const newMediaTab = this.mediaTabs[id]; const newMediaTab = this.mediaTabs[id];
this.tabSelected = tabContent.firstElementChild as HTMLDivElement; if(this.onChangeTab) {
this.onChangeTab(newMediaTab);
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(newMediaTab.scroll) { const fromMediaTab = this.mediaTab;
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)`;
}
}
this.mediaTab = newMediaTab; this.mediaTab = newMediaTab;
if(this.onChangeTab) { if(this.prevTabId !== -1 && animate) {
this.onChangeTab(this.mediaTab); this.onTransitionStart();
}
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) { /* if(this.prevTabId !== -1 && nav.offsetTop) {
@ -236,7 +257,7 @@ export default class AppSearchSuper {
/* this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount); /* this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount);
this.scroll.setVirtualContainer(this.sharedMediaSelected); */ 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.contentContainer.classList.remove('loaded');
this.load(true); this.load(true);
} }
@ -247,7 +268,7 @@ export default class AppSearchSuper {
//console.log('what y', this.tabSelected.style.transform); //console.log('what y', this.tabSelected.style.transform);
if(this.mediaTab.scroll !== undefined) { if(this.mediaTab.scroll !== undefined) {
this.tabSelected.style.transform = ''; this.mediaTab.contentTab.style.transform = '';
this.scrollable.scrollTop = this.mediaTab.scroll.scrollTop; this.scrollable.scrollTop = this.mediaTab.scroll.scrollTop;
} }
@ -1097,6 +1118,7 @@ export default class AppSearchSuper {
} }
let firstMediaTab: SearchSuperMediaTab; let firstMediaTab: SearchSuperMediaTab;
let count = 0;
mediaTabs.forEach(mediaTab => { mediaTabs.forEach(mediaTab => {
const counter = counters.find(c => c.filter._ === mediaTab.inputFilter); const counter = counters.find(c => c.filter._ === mediaTab.inputFilter);
@ -1107,6 +1129,8 @@ export default class AppSearchSuper {
if(counter.count && firstMediaTab === undefined) { if(counter.count && firstMediaTab === undefined) {
firstMediaTab = mediaTab; firstMediaTab = mediaTab;
} }
if(counter.count) ++count;
}); });
const membersTab = this.mediaTabsMap.get('members'); const membersTab = this.mediaTabsMap.get('members');
@ -1120,8 +1144,11 @@ export default class AppSearchSuper {
this.container.classList.toggle('hide', !firstMediaTab); this.container.classList.toggle('hide', !firstMediaTab);
this.container.parentElement.classList.toggle('search-empty', !firstMediaTab); this.container.parentElement.classList.toggle('search-empty', !firstMediaTab);
if(firstMediaTab) { if(firstMediaTab) {
this.skipScroll = true;
this.selectTab(this.mediaTabs.indexOf(firstMediaTab), false); this.selectTab(this.mediaTabs.indexOf(firstMediaTab), false);
firstMediaTab.menuTab.classList.add('active'); firstMediaTab.menuTab.classList.add('active');
this.navScrollableContainer.classList.toggle('hide', count <= 1);
} }
} }

View File

@ -217,9 +217,10 @@ function wrapVoiceMessage(audioEl: AudioElement) {
e.preventDefault(); e.preventDefault();
if(!audio.paused) { if(!audio.paused) {
audio.pause(); audio.pause();
scrub(e);
mousedown = true;
} }
scrub(e);
mousedown = true;
}); });
progress.addEventListener('mouseup', (e) => { progress.addEventListener('mouseup', (e) => {
if (mousemove && mousedown) { if (mousemove && mousedown) {
@ -437,7 +438,7 @@ export default class AudioElement extends HTMLElement {
const getDownloadPromise = () => appDocsManager.downloadDoc(doc); const getDownloadPromise = () => appDocsManager.downloadDoc(doc);
if(isVoice) { if(isRealVoice) {
if(!preloader) { if(!preloader) {
preloader = new ProgressivePreloader({ preloader = new ProgressivePreloader({
cancelable: true cancelable: true

View File

@ -168,6 +168,7 @@ export default class ChatBubbles {
const message = this.chat.getMessage(mid); const message = this.chat.getMessage(mid);
if(+bubble.dataset.timestamp >= (message.date + serverTimeManager.serverTimeOffset - 1)) { if(+bubble.dataset.timestamp >= (message.date + serverTimeManager.serverTimeOffset - 1)) {
this.bubbleGroups.addBubble(bubble, message, false);
return; return;
} }
@ -1025,6 +1026,7 @@ export default class ChatBubbles {
// if scroll down after search // if scroll down after search
if(history.indexOf(historyStorage.maxId) !== -1) { if(history.indexOf(historyStorage.maxId) !== -1) {
this.scrollable.loadedAll.bottom = true;
return; return;
} }
@ -1414,6 +1416,10 @@ export default class ChatBubbles {
const samePeer = this.peerId === peerId; 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); const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
let topMessage = this.chat.type === 'pinned' ? this.appMessagesManager.pinnedMessages[peerId].maxId : historyStorage.maxId ?? 0; let topMessage = this.chat.type === 'pinned' ? this.appMessagesManager.pinnedMessages[peerId].maxId : historyStorage.maxId ?? 0;
const isTarget = lastMsgId !== undefined; const isTarget = lastMsgId !== undefined;
@ -1497,9 +1503,15 @@ export default class ChatBubbles {
this.isFirstLoad = true; this.isFirstLoad = true;
} }
const oldChatInner = this.chatInner;
this.cleanup(); this.cleanup();
this.chatInner = document.createElement('div'); 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(); this.lazyLoadQueue.lock();
@ -1590,6 +1602,8 @@ export default class ChatBubbles {
this.scrollable.scrollTop = 99999; this.scrollable.scrollTop = 99999;
} }
this.onScroll();
this.chat.dispatchEvent('setPeer', lastMsgId, !isJump); this.chat.dispatchEvent('setPeer', lastMsgId, !isJump);
// warning // warning
@ -1718,7 +1732,7 @@ export default class ChatBubbles {
public setBubblePosition(bubble: HTMLElement, message: any, reverse: boolean) { public setBubblePosition(bubble: HTMLElement, message: any, reverse: boolean) {
const dateMessage = this.getDateContainerByMessage(message, reverse); 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; const offset = this.stickyIntersector ? 2 : 1;
let children = Array.from(dateMessage.container.children).slice(offset) as HTMLElement[]; let children = Array.from(dateMessage.container.children).slice(offset) as HTMLElement[];
let i = 0, foundMidOnSameTimestamp = 0; let i = 0, foundMidOnSameTimestamp = 0;

View File

@ -206,6 +206,8 @@ export default class Chat extends EventListenerBase<{
if(!samePeer) { if(!samePeer) {
rootScope.broadcast('peer_changing', this); rootScope.broadcast('peer_changing', this);
this.peerId = peerId; this.peerId = peerId;
} else if(this.setPeerPromise) {
return;
} }
//console.time('appImManager setPeer'); //console.time('appImManager setPeer');

View File

@ -157,9 +157,6 @@ export default class ChatInput {
this.chatInput.append(this.inputContainer); this.chatInput.append(this.inputContainer);
this.goDownBtn = Button('bubbles-go-down btn-corner btn-circle z-depth-1 hide', {icon: 'arrow_down'}); 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); this.inputContainer.append(this.goDownBtn);
attachClickEvent(this.goDownBtn, (e) => { attachClickEvent(this.goDownBtn, (e) => {
@ -257,6 +254,10 @@ export default class ChatInput {
this.inputMessageContainer.classList.add('input-message-container'); this.inputMessageContainer.classList.add('input-message-container');
if(this.chat.type === 'chat') { 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 = ButtonIcon('scheduled', {noRipple: true});
this.btnScheduled.classList.add('btn-scheduled', 'hide'); this.btnScheduled.classList.add('btn-scheduled', 'hide');
@ -376,7 +377,7 @@ export default class ChatInput {
this.inputContainer.append(this.btnCancelRecord, this.btnSendContainer); 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.onOpen.push(this.onEmoticonsOpen);
emoticonsDropdown.events.onClose.push(this.onEmoticonsClose); emoticonsDropdown.events.onClose.push(this.onEmoticonsClose);

View File

@ -8,7 +8,7 @@ import { isTouchSupported } from "../../helpers/touchSupport";
import appChatsManager from "../../lib/appManagers/appChatsManager"; import appChatsManager from "../../lib/appManagers/appChatsManager";
import appImManager from "../../lib/appManagers/appImManager"; import appImManager from "../../lib/appManagers/appImManager";
import rootScope from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import { blurActiveElement, whichChild } from "../../helpers/dom"; import { attachClickEvent, blurActiveElement, whichChild } from "../../helpers/dom";
import animationIntersector from "../animationIntersector"; import animationIntersector from "../animationIntersector";
import { horizontalMenu } from "../horizontalMenu"; import { horizontalMenu } from "../horizontalMenu";
import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue"; import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue";
@ -24,6 +24,7 @@ import AppGifsTab from "../sidebarRight/tabs/gifs";
import AppStickersTab from "../sidebarRight/tabs/stickers"; import AppStickersTab from "../sidebarRight/tabs/stickers";
import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpClassName from "../../helpers/dom/findUpClassName";
import findUpTag from "../../helpers/dom/findUpTag"; import findUpTag from "../../helpers/dom/findUpTag";
import ListenerSetter from "../../helpers/listenerSetter";
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
@ -32,7 +33,9 @@ export interface EmoticonsTab {
onCloseAfterTimeout?: () => void onCloseAfterTimeout?: () => void
} }
const test = false; const KEEP_OPEN = false;
const TOGGLE_TIMEOUT = 200;
const ANIMATION_DURATION = 200;
export class EmoticonsDropdown { export class EmoticonsDropdown {
public static lazyLoadQueue = new LazyLoadQueue(); public static lazyLoadQueue = new LazyLoadQueue();
@ -72,36 +75,36 @@ export class EmoticonsDropdown {
this.element = document.getElementById('emoji-dropdown') as HTMLDivElement; this.element = document.getElementById('emoji-dropdown') as HTMLDivElement;
} }
public attachButtonListener(button: HTMLElement) { public attachButtonListener(button: HTMLElement, listenerSetter: ListenerSetter) {
let firstTime = true; let firstTime = true;
if(isTouchSupported) { if(isTouchSupported) {
button.addEventListener('click', () => { attachClickEvent(button, () => {
if(firstTime) { if(firstTime) {
firstTime = false; firstTime = false;
this.toggle(true); this.toggle(true);
} else { } else {
this.toggle(); this.toggle();
} }
}); }, {listenerSetter});
} else { } else {
button.onmouseover = (e) => { listenerSetter.add(button, 'mouseover', (e) => {
//console.log('onmouseover button'); //console.log('onmouseover button');
if(firstTime) {
listenerSetter.add(button, 'mouseout', this.onMouseOut);
firstTime = false;
}
clearTimeout(this.displayTimeout); clearTimeout(this.displayTimeout);
//this.displayTimeout = setTimeout(() => { this.displayTimeout = window.setTimeout(() => {
if(firstTime) {
button.onmouseout = this.onMouseOut;
firstTime = false;
}
this.toggle(true); this.toggle(true);
//}, 0/* 200 */); }, TOGGLE_TIMEOUT);
}; });
} }
} }
private onMouseOut = (e: MouseEvent) => { private onMouseOut = (e: MouseEvent) => {
if(test) return; if(KEEP_OPEN) return;
clearTimeout(this.displayTimeout);
if(!this.element.classList.contains('active')) return; if(!this.element.classList.contains('active')) return;
const toElement = (e as any).toElement as Element; const toElement = (e as any).toElement as Element;
@ -109,10 +112,9 @@ export class EmoticonsDropdown {
return; return;
} }
clearTimeout(this.displayTimeout);
this.displayTimeout = window.setTimeout(() => { this.displayTimeout = window.setTimeout(() => {
this.toggle(false); this.toggle(false);
}, 200); }, TOGGLE_TIMEOUT);
}; };
private init() { private init() {
@ -265,7 +267,7 @@ export class EmoticonsDropdown {
this.container.classList.remove('disable-hover'); this.container.classList.remove('disable-hover');
this.events.onOpenAfter.forEach(cb => cb()); this.events.onOpenAfter.forEach(cb => cb());
}, isTouchSupported ? 0 : 200); }, isTouchSupported ? 0 : ANIMATION_DURATION);
// ! can't use together with resizeObserver // ! can't use together with resizeObserver
/* if(isTouchSupported) { /* if(isTouchSupported) {
@ -302,7 +304,7 @@ export class EmoticonsDropdown {
this.container.classList.remove('disable-hover'); this.container.classList.remove('disable-hover');
this.events.onCloseAfter.forEach(cb => cb()); this.events.onCloseAfter.forEach(cb => cb());
}, isTouchSupported ? 0 : 200); }, isTouchSupported ? 0 : ANIMATION_DURATION);
/* if(isTouchSupported) { /* if(isTouchSupported) {
const scrollHeight = this.container.scrollHeight; const scrollHeight = this.container.scrollHeight;

View File

@ -209,37 +209,44 @@ export default class StickersTab implements EmoticonsTab {
//console.log('got stickerSet', stickerSet, li); //console.log('got stickerSet', stickerSet, li);
if(stickerSet.set.thumbs?.length) { if(stickerSet.set.thumbs?.length) {
const downloadOptions = appStickersManager.getStickerSetThumbDownloadOptions(stickerSet.set); EmoticonsDropdown.lazyLoadQueue.push({
const promise = appDownloadManager.download(downloadOptions); div: button,
load: () => {
const downloadOptions = appStickersManager.getStickerSetThumbDownloadOptions(stickerSet.set);
const promise = appDownloadManager.download(downloadOptions);
if(stickerSet.set.pFlags.animated) { if(stickerSet.set.pFlags.animated) {
promise return promise
.then(readBlobAsText) .then(readBlobAsText)
//.then(JSON.parse) //.then(JSON.parse)
.then(json => { .then(json => {
lottieLoader.loadAnimationWorker({ lottieLoader.loadAnimationWorker({
container: button, container: button,
loop: true, loop: true,
autoplay: false, autoplay: false,
animationData: json, animationData: json,
width: 32, width: 32,
height: 32, height: 32,
needUpscale: true needUpscale: true
}, EMOTICONSSTICKERGROUP); }, EMOTICONSSTICKERGROUP);
}); });
} else { } else {
const image = new Image(); const image = new Image();
promise.then(blob => {
renderImageFromUrl(image, URL.createObjectURL(blob), () => { return promise.then(blob => {
button.append(image); renderImageFromUrl(image, URL.createObjectURL(blob), () => {
}); button.append(image);
}); });
} });
}
}
});
} else if(stickerSet.documents[0]._ !== 'documentEmpty') { // as thumb will be used first sticker } else if(stickerSet.documents[0]._ !== 'documentEmpty') { // as thumb will be used first sticker
wrapSticker({ wrapSticker({
doc: stickerSet.documents[0], doc: stickerSet.documents[0],
div: button as any, div: button as any,
group: EMOTICONSSTICKERGROUP group: EMOTICONSSTICKERGROUP,
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue
}); // kostil }); // kostil
} }
} }

View File

@ -12,7 +12,7 @@ import { fastRaf } from "../helpers/schedulers";
import { FocusDirection } from "../helpers/fastSmoothScroll"; import { FocusDirection } from "../helpers/fastSmoothScroll";
import findUpAsChild from "../helpers/dom/findUpAsChild"; 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); const selectTab = TransitionSlider(content, tabs || content.dataset.animation === 'tabs' ? 'tabs' : 'navigation', transitionTime, onTransitionEnd);
if(tabs) { if(tabs) {
@ -29,7 +29,12 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
const selectTarget = (target: HTMLElement, id: number, animate = true) => { const selectTarget = (target: HTMLElement, id: number, animate = true) => {
const tabContent = content.children[id] as HTMLDivElement; 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) { if(scrollableX) {
scrollableX.scrollIntoViewNew(target.parentElement.children[id] as HTMLElement, 'center', undefined, undefined, animate ? undefined : FocusDirection.Static, transitionTime, 'x'); scrollableX.scrollIntoViewNew(target.parentElement.children[id] as HTMLElement, 'center', undefined, undefined, animate ? undefined : FocusDirection.Static, transitionTime, 'x');

View File

@ -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) { if(label) {
this.label = document.createElement('label'); this.label = document.createElement('label');
this.setLabel(); this.setLabel();

View File

@ -43,7 +43,7 @@ export default class InputSearch {
this.input.addEventListener('input', this.onInput); this.input.addEventListener('input', this.onInput);
this.clearBtn.addEventListener('click', this.onClearClick); this.clearBtn.addEventListener('click', this.onClearClick);
this.container.append(this.input, searchIcon, this.clearBtn); this.container.append(searchIcon, this.clearBtn);
} }
onInput = () => { onInput = () => {

View File

@ -250,8 +250,8 @@ export class ScrollableX extends ScrollableBase {
if(!isTouchSupported) { if(!isTouchSupported) {
const scrollHorizontally = (e: any) => { const scrollHorizontally = (e: any) => {
if(!e.deltaX) { if(!e.deltaX && this.container.scrollWidth > this.container.clientWidth) {
this.container!.scrollLeft += e.deltaY / 4; this.container.scrollLeft += e.deltaY / 4;
cancelEvent(e); cancelEvent(e);
} }
}; };

View File

@ -46,10 +46,10 @@ import Scrollable from "../../scrollable";
import { isTouchSupported } from "../../../helpers/touchSupport"; import { isTouchSupported } from "../../../helpers/touchSupport";
let setText = (text: string, row: Row) => { let setText = (text: string, row: Row) => {
fastRaf(() => { //fastRaf(() => {
row.title.innerHTML = text; row.title.innerHTML = text;
row.container.style.display = ''; row.container.style.display = '';
}); //});
}; };
type ListLoaderResult<T> = {count: number, items: any[]}; type ListLoaderResult<T> = {count: number, items: any[]};

View File

@ -12,8 +12,8 @@
const App = { const App = {
id: 1025907, id: 1025907,
hash: '452b0359b988148995f22ff0f4229750', hash: '452b0359b988148995f22ff0f4229750',
version: '0.4.1', version: '0.4.2',
langPackVersion: '0.1.3', langPackVersion: '0.1.4',
langPack: 'macos', langPack: 'macos',
langPackCode: 'en', langPackCode: 'en',
domains: [] as string[], domains: [] as string[],

View File

@ -91,6 +91,15 @@ const lang = {
"Profile": "Profile", "Profile": "Profile",
"Saved": "Saved", "Saved": "Saved",
"ReportBug": "Report Bug", "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 // * android
"ActionCreateChannel": "Channel created", "ActionCreateChannel": "Channel created",

View File

@ -477,7 +477,7 @@ export class AppChatsManager {
apiUpdatesManager.processUpdateMessage(updates); apiUpdatesManager.processUpdateMessage(updates);
const channelId = updates.chats[0].id; const channelId = updates.chats[0].id;
rootScope.broadcast('history_focus', -channelId); rootScope.broadcast('history_focus', {peerId: -channelId});
return channelId; return channelId;
}); });
@ -503,7 +503,7 @@ export class AppChatsManager {
apiUpdatesManager.processUpdateMessage(updates); apiUpdatesManager.processUpdateMessage(updates);
const chatId = (updates as any as Updates.updates).chats[0].id; const chatId = (updates as any as Updates.updates).chats[0].id;
rootScope.broadcast('history_focus', -chatId); rootScope.broadcast('history_focus', {peerId: -chatId});
return chatId; return chatId;
}); });

View File

@ -233,6 +233,7 @@ export class AppDialogsManager {
}; };
this.setListClickListener(archivedChatList, null, true); this.setListClickListener(archivedChatList, null, true);
//this.setListClickListener(archivedChatList, null, true); // * to test peer changing
this.chatsPreloader = putPreloader(null, true); this.chatsPreloader = putPreloader(null, true);

View File

@ -169,8 +169,9 @@ export class AppImManager {
} }
}); });
rootScope.on('history_focus', (peerId) => { rootScope.on('history_focus', (e) => {
this.setInnerPeer(peerId); const {peerId, mid} = e;
this.setInnerPeer(peerId, mid);
}); });
rootScope.on('peer_changing', (chat) => { rootScope.on('peer_changing', (chat) => {

View File

@ -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 = [ const goodMedias = [
'messageMediaPhoto', 'messageMediaPhoto',
'messageMediaDocument', 'messageMediaDocument',
@ -2992,7 +2996,7 @@ export class AppMessagesManager {
return true; 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; return false;
} }
@ -3005,7 +3009,7 @@ export class AppMessagesManager {
|| message.fromId === rootScope.myId || message.fromId === rootScope.myId
|| appChatsManager.getChat(message.peerId)._ === 'chat' || appChatsManager.getChat(message.peerId)._ === 'chat'
|| appChatsManager.hasRights(message.peerId, 'delete_messages') || appChatsManager.hasRights(message.peerId, 'delete_messages')
); ) && !message.pFlags.is_outgoing;
} }
public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) { public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) {
@ -4919,60 +4923,31 @@ export class AppMessagesManager {
peerTypeNotifySettings: PeerNotifySettings peerTypeNotifySettings: PeerNotifySettings
}> = {}) { }> = {}) {
const peerId = this.getMessagePeer(message); const peerId = this.getMessagePeer(message);
let peerString: string;
const notification: NotifyOptions = {}; const notification: NotifyOptions = {};
var notificationMessage: string, const peerString = appPeersManager.getPeerString(peerId);
notificationPhoto: any; let notificationMessage: string;
const _ = (str: string) => str;
const localSettings = appNotificationsManager.getLocalSettings();
if(options.peerTypeNotifySettings.show_previews) { if(options.peerTypeNotifySettings.show_previews) {
if(message._ === 'message' && message.fwd_from && options.fwdCount) { 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 { } else {
notificationMessage = this.wrapMessageForReply(message, undefined, undefined, true); notificationMessage = this.wrapMessageForReply(message, undefined, undefined, true);
} }
} else { } else {
notificationMessage = 'New notification'; notificationMessage = I18n.format('Notifications.New', true);
} }
if(peerId > 0) { notification.title = appPeersManager.getPeerTitle(peerId, true);
const fromUser = appUsersManager.getUser(message.fromId); if(peerId < 0 && message.fromId !== message.peerId) {
const fromPhoto = appUsersManager.getUserPhoto(message.fromId); notification.title = appPeersManager.getPeerTitle(message.fromId, true) +
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; notification.title;
}
notificationPhoto = appChatsManager.getChatPhoto(-peerId);
peerString = appChatsManager.getChatString(-peerId);
} }
notification.title = RichTextProcessor.wrapPlainText(notification.title); notification.title = RichTextProcessor.wrapPlainText(notification.title);
notification.onclick = () => { notification.onclick = () => {
/* rootScope.broadcast('history_focus', { rootScope.broadcast('history_focus', {peerId, mid: message.mid});
peerString: peerString,
messageID: message.flags & 16 ? message.mid : 0
}); */
}; };
notification.message = notificationMessage; notification.message = notificationMessage;
@ -4980,16 +4955,17 @@ export class AppMessagesManager {
notification.tag = peerString; notification.tag = peerString;
notification.silent = true;//message.pFlags.silent || false; notification.silent = true;//message.pFlags.silent || false;
/* if(notificationPhoto.location && !notificationPhoto.location.empty) { const peerPhoto = appPeersManager.getPeerPhoto(peerId);
apiManager.downloadSmallFile(notificationPhoto.location, notificationPhoto.size).then(function (blob) { if(peerPhoto) {
if (message.pFlags.unread) { appProfileManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => {
notification.image = blob if(message.pFlags.unread) {
NotificationsManager.notify(notification) notification.image = url;
appNotificationsManager.notify(notification);
} }
}) });
} else { */ } else {
appNotificationsManager.notify(notification); appNotificationsManager.notify(notification);
//} }
} }
public getScheduledMessagesStorage(peerId: number) { public getScheduledMessagesStorage(peerId: number) {

View File

@ -17,6 +17,7 @@ import { copy, deepEqual } from "../../helpers/object";
import { convertInputKeyToKey } from "../../helpers/string"; import { convertInputKeyToKey } from "../../helpers/string";
import { isMobile } from "../../helpers/userAgent"; import { isMobile } from "../../helpers/userAgent";
import { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySettings, Update } from "../../layer"; import { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySettings, Update } from "../../layer";
import I18n from "../langPack";
import apiManager from "../mtproto/mtprotoworker"; import apiManager from "../mtproto/mtprotoworker";
import rootScope from "../rootScope"; import rootScope from "../rootScope";
import sessionStorage from "../sessionStorage"; import sessionStorage from "../sessionStorage";
@ -55,10 +56,9 @@ export class AppNotificationsManager {
notifyChats: null as ImSadAboutIt, notifyChats: null as ImSadAboutIt,
notifyBroadcasts: null as ImSadAboutIt notifyBroadcasts: null as ImSadAboutIt
}; };
private exceptions: {[peerId: string]: PeerNotifySettings} = {}; //private exceptions: {[peerId: string]: PeerNotifySettings} = {};
private notifyContactsSignUp: Promise<boolean>; private notifyContactsSignUp: Promise<boolean>;
private faviconEl: HTMLLinkElement = document.head.querySelector('link[rel="icon"]'); private faviconEl: HTMLLinkElement = document.head.querySelector('link[rel="icon"]');
private langNotificationsPluralize = 'notifications';//_.pluralize('page_title_pluralize_notifications');
private titleBackup = document.title; private titleBackup = document.title;
private titleChanged = false; private titleChanged = false;
@ -221,7 +221,7 @@ export class AppNotificationsManager {
resetTitle(); resetTitle();
} else { } else {
this.titleChanged = true; 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'); //this.setFavicon('assets/img/favicon_unread.ico');
// fetch('assets/img/favicon.ico') // fetch('assets/img/favicon.ico')
@ -499,8 +499,8 @@ export class AppNotificationsManager {
data.image = FileManager.getUrl(data.image, 'image/jpeg') data.image = FileManager.getUrl(data.image, 'image/jpeg')
} }
} }
else if (!data.image) */ { else */ if(!data.image) {
data.image = 'assets/img/logo.svg'; data.image = 'assets/img/logo_filled_rounded.png';
} }
// console.log('notify image', data.image) // console.log('notify image', data.image)

View File

@ -37,7 +37,7 @@ export class AppProfileManager {
private savedAvatarURLs: { private savedAvatarURLs: {
[peerId: number]: { [peerId: number]: {
[size in PeerPhotoSize]?: string | Promise<any> [size in PeerPhotoSize]?: string | Promise<string>
} }
} = {}; } = {};
@ -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); const inputPeer = appPeersManager.getInputPeerById(peerId);
let needFadeIn = true; let cached = false;
let getAvatarPromise: Promise<any>; let getAvatarPromise: Promise<string>;
let saved = this.savedAvatarURLs[peerId]; let saved = this.savedAvatarURLs[peerId];
if(!saved || !saved[size]) { if(!saved || !saved[size]) {
if(!saved) { if(!saved) {
@ -489,7 +489,7 @@ export class AppProfileManager {
const promise = appDownloadManager.download(downloadOptions); const promise = appDownloadManager.download(downloadOptions);
getAvatarPromise = saved[size] = promise.then(blob => { getAvatarPromise = saved[size] = promise.then(blob => {
saved[size] = URL.createObjectURL(blob); return saved[size] = URL.createObjectURL(blob);
/* if(str) { /* if(str) {
console.log(str, Date.now() / 1000, Date.now() - time); console.log(str, Date.now() / 1000, Date.now() - time);
@ -498,12 +498,18 @@ export class AppProfileManager {
} else if(typeof(saved[size]) !== 'string') { } else if(typeof(saved[size]) !== 'string') {
getAvatarPromise = saved[size] as Promise<any>; getAvatarPromise = saved[size] as Promise<any>;
} else { } else {
getAvatarPromise = Promise.resolve(); getAvatarPromise = Promise.resolve(saved[size]);
needFadeIn = false; 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; let callback: () => void;
if(!needFadeIn) { if(cached) {
// смотри в misc.ts: renderImageFromUrl // смотри в misc.ts: renderImageFromUrl
callback = () => { callback = () => {
replaceContent(div, img); replaceContent(div, img);
@ -532,16 +538,16 @@ export class AppProfileManager {
}; };
} }
const loadPromise = getAvatarPromise.then(() => { const renderPromise = loadPromise.then((url) => {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
renderImageFromUrl(img, saved[size] as string, () => { renderImageFromUrl(img, url, () => {
callback(); callback();
resolve(); resolve();
}/* , false */); }/* , false */);
}); });
}); });
return {cached: !needFadeIn, loadPromise}; return {cached, loadPromise: renderPromise};
} }
// peerId === peerId || title // peerId === peerId || title

View File

@ -74,6 +74,7 @@ export type MTMessage = InvokeApiOptions & MTMessageOptions & {
}; };
const CONNECTION_TIMEOUT = 5000; const CONNECTION_TIMEOUT = 5000;
let invokeAfterMsgConstructor: number;
export default class MTPNetworker { export default class MTPNetworker {
private authKeyUint8: Uint8Array; private authKeyUint8: Uint8Array;
@ -320,15 +321,21 @@ export default class MTPNetworker {
} }
if(options.afterMessageId) { if(options.afterMessageId) {
const invokeAfterMsg = Schema.API.methods.find(m => m.method === 'invokeAfterMsg'); if(invokeAfterMsgConstructor === undefined) {
if(!invokeAfterMsg) throw new Error('no invokeAfterMsg!'); const m = Schema.API.methods.find(m => m.method === 'invokeAfterMsg');
invokeAfterMsgConstructor = m ? +m.id >>> 0 : 0;
if(this.debug) { }
this.log('Api call options.afterMessageId!');
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); options.resultType = serializer.storeMethod(method, params);

View File

@ -9,6 +9,7 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
*/ */
import { MOUNT_CLASS_TO } from '../../config/debug';
import { tsNow } from '../../helpers/date'; import { tsNow } from '../../helpers/date';
import sessionStorage from '../sessionStorage'; 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;

View File

@ -51,7 +51,7 @@ export type BroadcastEvents = {
'history_delete': {peerId: number, msgs: {[mid: number]: true}}, 'history_delete': {peerId: number, msgs: {[mid: number]: true}},
'history_forbidden': number, 'history_forbidden': number,
'history_reload': number, 'history_reload': number,
'history_focus': number, 'history_focus': {peerId: number, mid?: number},
//'history_request': void, //'history_request': void,
'message_edit': {storage: MessagesStorage, peerId: number, mid: number}, 'message_edit': {storage: MessagesStorage, peerId: number, mid: number},

View File

@ -17,6 +17,7 @@ import TrackingMonkey from '../components/monkeys/tracking';
import CodeInputField from '../components/codeInputField'; import CodeInputField from '../components/codeInputField';
import { replaceContent } from '../helpers/dom'; import { replaceContent } from '../helpers/dom';
import { i18n, LangPackKey } from '../lib/langPack'; import { i18n, LangPackKey } from '../lib/langPack';
import { randomLong } from '../helpers/random';
let authCode: AuthSentCode.authSentCode = null; let authCode: AuthSentCode.authSentCode = null;
@ -29,7 +30,7 @@ let onFirstMount = (): Promise<any> => {
const codeInputField = new CodeInputField({ const codeInputField = new CodeInputField({
label: 'Code', label: 'Code',
name: 'code', name: randomLong(),
length: CODELENGTH, length: CODELENGTH,
onFill: (code) => { onFill: (code) => {
submitCode('' + code); submitCode('' + code);

View File

@ -25,6 +25,8 @@ import { LangPackString } from "../layer";
import lottieLoader from "../lib/lottieLoader"; import lottieLoader from "../lib/lottieLoader";
import { ripple } from "../components/ripple"; import { ripple } from "../components/ripple";
import findUpTag from "../helpers/dom/findUpTag"; import findUpTag from "../helpers/dom/findUpTag";
import findUpClassName from "../helpers/dom/findUpClassName";
import { randomLong } from "../helpers/random";
type Country = _Country & { type Country = _Country & {
li?: HTMLLIElement[] li?: HTMLLIElement[]
@ -56,14 +58,14 @@ let onFirstMount = () => {
const countryInputField = new InputField({ const countryInputField = new InputField({
label: 'Login.CountrySelectorLabel', label: 'Login.CountrySelectorLabel',
name: 'countryCode', name: randomLong(),
plainText: true plainText: true
}); });
countryInputField.container.classList.add('input-select'); countryInputField.container.classList.add('input-select');
const countryInput = countryInputField.input as HTMLInputElement; const countryInput = countryInputField.input as HTMLInputElement;
countryInput.autocomplete = 'rrRandomRR'; countryInput.autocomplete = randomLong();
const selectWrapper = document.createElement('div'); const selectWrapper = document.createElement('div');
selectWrapper.classList.add('select-wrapper', 'z-depth-3', 'hide'); selectWrapper.classList.add('select-wrapper', 'z-depth-3', 'hide');
@ -77,38 +79,25 @@ let onFirstMount = () => {
const scroll = new Scrollable(selectWrapper); const scroll = new Scrollable(selectWrapper);
let initedSelect = false;
let initSelect = () => { let initSelect = () => {
initSelect = null; initSelect = null;
countries.forEach((c) => { countries.forEach((c) => {
initedSelect = true; const emoji = c.emoji;
/* let unified = unifiedCountryCodeEmoji(c.code); const liArr: Array<HTMLLIElement> = [];
let emoji = unified.split('-').reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), ''); */
//let emoji = countryCodeEmoji(c.code);
let emoji = c.emoji;
let liArr: Array<HTMLLIElement> = [];
c.phoneCode.split(' and ').forEach((phoneCode: string) => { c.phoneCode.split(' and ').forEach((phoneCode: string) => {
let li = document.createElement('li'); const li = document.createElement('li');
var spanEmoji = document.createElement('span'); const spanEmoji = document.createElement('span');
/* spanEmoji.innerHTML = countryCodeEmoji(c.code); */
//spanEmoji.classList.add('emoji-outer', 'emoji-sizer');
//spanEmoji.innerHTML = `<span class="emoji-inner" style="background: url(${sheetUrl}${sheetNo}.png);background-position:${xPos}% ${yPos}%;background-size:${sizeX}% ${sizeY}%" data-codepoints="${unified}"></span>`;
let kek = RichTextProcessor.wrapRichText(emoji); const kek = RichTextProcessor.wrapRichText(emoji);
//console.log(c.name, emoji, kek, spanEmoji.innerHTML);
li.appendChild(spanEmoji); li.appendChild(spanEmoji);
spanEmoji.outerHTML = kek; spanEmoji.outerHTML = kek;
li.append(c.name); li.append(c.name);
var span = document.createElement('span'); const span = document.createElement('span');
span.classList.add('phone-code'); span.classList.add('phone-code');
span.innerText = '+' + phoneCode; span.innerText = '+' + phoneCode;
li.appendChild(span); li.appendChild(span);
@ -120,23 +109,32 @@ let onFirstMount = () => {
c.li = liArr; 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; let target = e.target as HTMLElement;
if(target.tagName !== 'LI') target = findUpTag(target, 'LI'); if(target.tagName !== 'LI') target = findUpTag(target, 'LI');
let countryName = target.childNodes[1].textContent;//target.innerText.split('\n').shift(); selectCountryByTarget(target);
let phoneCode = target.querySelector<HTMLElement>('.phone-code').innerText;
countryInput.value = countryName;
lastCountrySelected = countries.find(c => c.name === countryName);
telEl.value = lastValue = phoneCode;
setTimeout(() => telEl.focus(), 0);
//console.log('clicked', e, countryName, phoneCode); //console.log('clicked', e, countryName, phoneCode);
}); });
countryInputField.container.appendChild(selectWrapper); countryInputField.container.appendChild(selectWrapper);
}; };
const selectCountryByTarget = (target: HTMLElement) => {
const countryName = target.childNodes[1].textContent;//target.innerText.split('\n').shift();
const phoneCode = target.querySelector<HTMLElement>('.phone-code').innerText;
countryInput.value = countryName;
lastCountrySelected = countries.find(c => c.name === countryName);
telEl.value = lastValue = phoneCode;
hidePicker();
setTimeout(() => telEl.focus(), 0);
};
initSelect(); initSelect();
@ -152,6 +150,7 @@ let onFirstMount = () => {
} }
clearTimeout(hideTimeout); clearTimeout(hideTimeout);
hideTimeout = undefined;
selectWrapper.classList.remove('hide'); selectWrapper.classList.remove('hide');
void selectWrapper.offsetWidth; // reflow void selectWrapper.offsetWidth; // reflow
@ -171,9 +170,9 @@ let onFirstMount = () => {
let mouseDownHandlerAttached = false; let mouseDownHandlerAttached = false;
const onMouseDown = (e: MouseEvent) => { const onMouseDown = (e: MouseEvent) => {
/* if(findUpClassName(e.target, 'input-select')) { if(findUpClassName(e.target, 'input-select')) {
return; return;
} */ }
if(e.target === countryInput) { if(e.target === countryInput) {
return; return;
} }
@ -184,9 +183,11 @@ let onFirstMount = () => {
}; };
const hidePicker = () => { const hidePicker = () => {
if(hideTimeout !== undefined) return;
selectWrapper.classList.remove('active'); selectWrapper.classList.remove('active');
hideTimeout = window.setTimeout(() => { hideTimeout = window.setTimeout(() => {
selectWrapper.classList.add('hide'); selectWrapper.classList.add('hide');
hideTimeout = undefined;
}, 200); }, 200);
}; };
/* false && countryInput.addEventListener('blur', function(this: typeof countryInput, e) { /* false && countryInput.addEventListener('blur', function(this: typeof countryInput, e) {
@ -221,6 +222,8 @@ let onFirstMount = () => {
countries.forEach((c) => { countries.forEach((c) => {
c.li.forEach(li => li.style.display = ''); c.li.forEach(li => li.style.display = '');
}); });
} else if(matches.length === 1 && e.key === 'Enter') {
selectCountryByTarget(matches[0].li[0]);
} }
}); });

View File

@ -359,6 +359,10 @@
//overflow: visible!important; //overflow: visible!important;
opacity: .3; opacity: .3;
@include hover() {
opacity: 1;
}
&.active, .audio.is-unread:not(.is-out) & { &.active, .audio.is-unread:not(.is-out) & {
opacity: 1; opacity: 1;
} }

View File

@ -112,7 +112,7 @@
&.active { &.active {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transform: scale(1); transform: scale3d(1, 1, 1); // * scale3d (NOT scale) will fix jumping text
} }
&:not(.active) { &:not(.active) {
@ -178,7 +178,7 @@
@include hover-background-effect(); @include hover-background-effect();
&.danger { &.danger {
@include hover-background-effect(red); @include hover-background-effect(danger);
} }
&:before { &:before {

View File

@ -1041,12 +1041,6 @@ $chat-helper-size: 39px;
} }
//} //}
} }
.preloader-container {
.preloader-circular {
background-color: rgba(0, 0, 0, .3);
}
}
.search-group.search-group-messages { .search-group.search-group-messages {
padding: 0.25rem 0 .5rem; padding: 0.25rem 0 .5rem;
@ -1139,7 +1133,7 @@ $chat-helper-size: 39px;
position: absolute; position: absolute;
background-color: var(--surface-color); background-color: var(--surface-color);
border-radius: 50%; border-radius: 50%;
color: $placeholder-color; color: var(--secondary-text-color);
font-size: 1.5rem; font-size: 1.5rem;
display: flex; display: flex;
align-items: center; align-items: center;
@ -1171,6 +1165,10 @@ $chat-helper-size: 39px;
position: absolute; position: absolute;
top: -.25rem; top: -.25rem;
right: -.25rem; right: -.25rem;
&.badge-primary {
background-color: var(--chatlist-status-color);
}
@include respond-to(handhelds) { @include respond-to(handhelds) {
top: -.75rem; top: -.75rem;

View File

@ -1279,8 +1279,8 @@ $bubble-margin: .25rem;
visibility: visible; visibility: visible;
} }
.tgico-pinnedchat:before { .tgico-pinnedchat {
font-weight: bold; margin-right: .125rem;
} }
} }

View File

@ -11,6 +11,7 @@
.input-field { .input-field {
--height: 54px; --height: 54px;
--border-radius: #{$border-radius-medium};
position: relative; position: relative;
@include respond-to(handhelds) { @include respond-to(handhelds) {
@ -62,27 +63,39 @@
transition: .2s transform, .2s padding, .1s opacity, font-weight 0s .1s; 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 { &-input {
--padding: 1.0625rem; --padding: 1rem;
--padding-horizontal: 1rem; --padding-horizontal: 1rem;
--border-width: 1px; --border-width: 1px;
--border-width-top: 2px;
border: var(--border-width) solid var(--input-search-border-color); border: var(--border-width) solid var(--input-search-border-color);
border-radius: $border-radius-medium; border-radius: var(--border-radius);
background-color: transparent; background-color: transparent;
//padding: 0 1rem; //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; box-sizing: border-box;
width: 100%; width: 100%;
min-height: var(--height); min-height: var(--height);
transition: 0s border-color; transition: 0s border-color;
position: relative; position: relative;
z-index: 1; z-index: 1;
line-height: 1.3125; line-height: var(--line-height);
//line-height: calc(54px - var(--border-width));
/* overflow: hidden;
white-space: nowrap; */
@include respond-to(handhelds) { @include respond-to(handhelds) {
--padding: .9375rem; --padding: .9375rem;
@ -116,8 +129,6 @@
} */ } */
&:focus { &:focus {
--border-width: 2px;
--border-width-top: 3px;
border-color: var(--primary-color); border-color: var(--primary-color);
//padding: 0 calc(1rem - 1px); //padding: 0 calc(1rem - 1px);
} }
@ -133,6 +144,10 @@
& ~ label { & ~ label {
color: var(--danger-color) !important; color: var(--danger-color) !important;
} }
& ~ .input-field-border {
border-color: var(--danger-color) !important;
}
} }
&.valid { &.valid {
@ -141,6 +156,10 @@
& ~ label { & ~ label {
color: #26962F !important; color: #26962F !important;
} }
& ~ .input-field-border {
border-color: #26962F !important;
}
} }
/* &.error, &.valid { /* &.error, &.valid {
@ -158,11 +177,15 @@
font-weight: 500; 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, &:valid ~ label,
&:not(:empty):focus ~ label { &:not(:empty):focus ~ label {
transition-delay: 0s, 0s, 0s, 0s; transition-delay: 0s, 0s, 0s, 0s;
} } */
&:focus ~ label, &:focus ~ label,
&:valid ~ label, &:valid ~ label,
@ -252,6 +275,7 @@ input:focus, button:focus {
margin-left: .4375rem; margin-left: .4375rem;
margin-right: .4375rem; margin-right: .4375rem;
overflow: hidden; overflow: hidden;
--border-radius: 22px;
@include respond-to(handhelds) { @include respond-to(handhelds) {
margin-left: 1rem; margin-left: 1rem;
@ -265,7 +289,6 @@ input:focus, button:focus {
min-height: var(--height) !important; min-height: var(--height) !important;
max-height: var(--height) !important; max-height: var(--height) !important;
//line-height: calc(var(--height) + 2px - var(--border-width) * 2); //line-height: calc(var(--height) + 2px - var(--border-width) * 2);
border-radius: 22px;
border-color: var(--input-search-border-color); border-color: var(--input-search-border-color);
line-height: var(--height); line-height: var(--height);
@ -281,7 +304,6 @@ input:focus, button:focus {
} }
&:focus { &:focus {
--border-width: 2px;
background-color: transparent; background-color: transparent;
border-color: var(--primary-color); border-color: var(--primary-color);

View File

@ -70,7 +70,7 @@ $transition: .2s ease-in-out;
.preloader-circular { .preloader-circular {
animation: none; animation: none;
background-color: rgba(0, 0, 0, .7); background-color: rgba(0, 0, 0, .3);
border-radius: 50%; border-radius: 50%;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -173,6 +173,7 @@
justify-content: center; justify-content: center;
font-size: 1rem; font-size: 1rem;
line-height: var(--line-height); line-height: var(--line-height);
border-radius: 0;
} }
.menu-horizontal-div i { .menu-horizontal-div i {
@ -287,6 +288,10 @@
> div:first-child { > div:first-child {
transform: translateY(0); 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);
} }
} }

View File

@ -45,11 +45,16 @@
.btn-icon { .btn-icon {
position: absolute; position: absolute;
right: .5rem; right: .4375rem;
top: .5rem; top: .4375rem;
z-index: 1; z-index: 1;
opacity: 1; opacity: 1;
transition: opacity .2s ease; transition: opacity .2s ease;
@include respond-to(handhelds) {
right: .3125rem;
top: .3125rem;
}
} }
/* &:last-child:not(:nth-child(10)) { /* &:last-child:not(:nth-child(10)) {
.btn-icon { .btn-icon {
@ -65,7 +70,7 @@
} }
.poll-create-questions { .poll-create-questions {
padding: 0px 1.25rem 2.03125rem; padding: 0px 1.25rem 1.5rem;
.input-field-input { .input-field-input {
padding-right: 3.25rem; padding-right: 3.25rem;
@ -90,4 +95,4 @@
top: 54px; top: 54px;
right: 20px; right: 20px;
} }
} }