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:
parent
baa35fd35b
commit
198eea41ee
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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)`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const fromMediaTab = this.mediaTab;
|
||||
this.mediaTab = newMediaTab;
|
||||
|
||||
if(this.onChangeTab) {
|
||||
this.onChangeTab(this.mediaTab);
|
||||
if(this.prevTabId !== -1 && animate) {
|
||||
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) {
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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');
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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');
|
||||
if(firstTime) {
|
||||
listenerSetter.add(button, 'mouseout', this.onMouseOut);
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
clearTimeout(this.displayTimeout);
|
||||
//this.displayTimeout = setTimeout(() => {
|
||||
if(firstTime) {
|
||||
button.onmouseout = this.onMouseOut;
|
||||
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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);
|
||||
EmoticonsDropdown.lazyLoadQueue.push({
|
||||
div: button,
|
||||
load: () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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();
|
||||
|
@ -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 = () => {
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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<T> = {count: number, items: any[]};
|
||||
|
@ -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[],
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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 peerString = appPeersManager.getPeerString(peerId);
|
||||
let notificationMessage: string;
|
||||
|
||||
const _ = (str: string) => str;
|
||||
|
||||
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) {
|
||||
|
@ -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<boolean>;
|
||||
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)
|
||||
|
||||
|
@ -37,7 +37,7 @@ export class AppProfileManager {
|
||||
|
||||
private savedAvatarURLs: {
|
||||
[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);
|
||||
|
||||
let needFadeIn = true;
|
||||
let getAvatarPromise: Promise<any>;
|
||||
let cached = false;
|
||||
let getAvatarPromise: Promise<string>;
|
||||
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<any>;
|
||||
} 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<void>((resolve) => {
|
||||
renderImageFromUrl(img, saved[size] as string, () => {
|
||||
renderImageFromUrl(img, url, () => {
|
||||
callback();
|
||||
resolve();
|
||||
}/* , false */);
|
||||
});
|
||||
});
|
||||
|
||||
return {cached: !needFadeIn, loadPromise};
|
||||
return {cached, loadPromise: renderPromise};
|
||||
}
|
||||
|
||||
// peerId === peerId || title
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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},
|
||||
|
@ -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<any> => {
|
||||
|
||||
const codeInputField = new CodeInputField({
|
||||
label: 'Code',
|
||||
name: 'code',
|
||||
name: randomLong(),
|
||||
length: CODELENGTH,
|
||||
onFill: (code) => {
|
||||
submitCode('' + code);
|
||||
|
@ -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;
|
||||
const emoji = c.emoji;
|
||||
|
||||
/* 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;
|
||||
|
||||
let liArr: Array<HTMLLIElement> = [];
|
||||
const liArr: Array<HTMLLIElement> = [];
|
||||
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 = `<span class="emoji-inner" style="background: url(${sheetUrl}${sheetNo}.png);background-position:${xPos}% ${yPos}%;background-size:${sizeX}% ${sizeY}%" data-codepoints="${unified}"></span>`;
|
||||
|
||||
|
||||
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<HTMLElement>('.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<HTMLElement>('.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]);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -359,6 +359,10 @@
|
||||
//overflow: visible!important;
|
||||
opacity: .3;
|
||||
|
||||
@include hover() {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.active, .audio.is-unread:not(.is-out) & {
|
||||
opacity: 1;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -1279,8 +1279,8 @@ $bubble-margin: .25rem;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.tgico-pinnedchat:before {
|
||||
font-weight: bold;
|
||||
.tgico-pinnedchat {
|
||||
margin-right: .125rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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%;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user