From b94dba3d9be6a4ce047d33ad351a09887679b2c3 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sat, 8 May 2021 21:46:50 +0400 Subject: [PATCH] Respect read comments position Fix bluring keyboard on Android Fix reading messages when page was blured Fix instant reading messages if there only one page of them Split dom functions --- src/components/animationIntersector.ts | 2 +- src/components/appMediaViewer.ts | 4 +- src/components/appNavigationController.ts | 3 +- src/components/appSearchSuper..ts | 4 +- src/components/appSelectPeers.ts | 3 +- src/components/audio.ts | 3 +- src/components/avatar.ts | 3 +- src/components/buttonMenu.ts | 3 +- src/components/buttonMenuToggle.ts | 3 +- src/components/chat/audio.ts | 3 +- src/components/chat/bubbles.ts | 132 ++-- src/components/chat/contextMenu.ts | 5 +- src/components/chat/dragAndDrop.ts | 4 +- src/components/chat/input.ts | 9 +- src/components/chat/markupTooltip.ts | 6 +- src/components/chat/pinnedContainer.ts | 3 +- src/components/chat/pinnedMessage.ts | 4 +- src/components/chat/search.ts | 3 +- src/components/chat/selection.ts | 5 +- src/components/chat/sendContextMenu.ts | 2 +- src/components/chat/topbar.ts | 4 +- src/components/emoticonsDropdown/index.ts | 4 +- src/components/gifsMasonry.ts | 2 +- src/components/horizontalMenu.ts | 2 +- src/components/inputField.ts | 4 +- src/components/languageChangeButton.ts | 9 +- src/components/misc.ts | 3 +- src/components/passwordInputField.ts | 2 +- src/components/peerTitle.ts | 2 +- src/components/poll.ts | 4 +- src/components/popups/createPoll.ts | 5 +- src/components/popups/index.ts | 2 +- src/components/popups/newMedia.ts | 4 +- src/components/popups/stickers.ts | 2 +- src/components/preloader.ts | 4 +- src/components/row.ts | 2 +- src/components/scrollable.ts | 2 +- src/components/sidebarLeft/index.ts | 5 +- src/components/sidebarLeft/tabs/2fa/email.ts | 4 +- .../sidebarLeft/tabs/2fa/emailConfirmation.ts | 5 +- .../sidebarLeft/tabs/2fa/enterPassword.ts | 5 +- src/components/sidebarLeft/tabs/2fa/hint.ts | 3 +- src/components/sidebarLeft/tabs/2fa/index.ts | 2 +- .../sidebarLeft/tabs/2fa/passwordSet.ts | 2 +- .../sidebarLeft/tabs/2fa/reEnterPassword.ts | 3 +- .../sidebarLeft/tabs/activeSessions.ts | 3 +- src/components/sidebarLeft/tabs/background.ts | 2 +- .../sidebarLeft/tabs/backgroundColor.ts | 5 +- .../sidebarLeft/tabs/blockedUsers.ts | 5 +- .../sidebarLeft/tabs/chatFolders.ts | 4 +- src/components/sidebarLeft/tabs/contacts.ts | 2 +- src/components/sidebarLeft/tabs/editFolder.ts | 1 - .../sidebarLeft/tabs/editProfile.ts | 2 +- .../sidebarLeft/tabs/generalSettings.ts | 2 +- .../sidebarLeft/tabs/privacyAndSecurity.ts | 2 +- src/components/sidebarRight/tabs/chatType.ts | 3 +- src/components/sidebarRight/tabs/editChat.ts | 3 +- .../sidebarRight/tabs/editContact.ts | 3 +- src/components/sidebarRight/tabs/gifs.ts | 2 +- .../sidebarRight/tabs/groupPermissions.ts | 3 +- .../sidebarRight/tabs/sharedMedia.ts | 4 +- src/components/sidebarRight/tabs/stickers.ts | 2 +- .../sidebarRight/tabs/userPermissions.ts | 6 +- src/components/slider.ts | 2 +- src/components/sortedUserList.ts | 4 +- src/components/swipeHandler.ts | 2 +- src/components/transition.ts | 2 +- src/components/wrappers.ts | 4 +- src/helpers/calcImageInBox.ts | 45 ++ src/helpers/dom.ts | 591 +----------------- src/helpers/dom/blurActiveElement.ts | 14 + src/helpers/dom/canFocus.ts | 11 + src/helpers/dom/cancelEvent.ts | 27 + src/helpers/dom/cancelSelection.ts | 19 + src/helpers/dom/clickEvent.ts | 53 ++ src/helpers/dom/disableTransition.ts | 15 + src/helpers/dom/getRichValue.ts | 152 +++++ src/helpers/dom/getSelectedNodes.ts | 27 + src/helpers/dom/getSelectedText.ts | 17 + src/helpers/dom/handleScrollSideEvent.ts | 48 ++ src/helpers/dom/htmlToDocumentFragment.ts | 12 + src/helpers/dom/htmlToSpan.ts | 11 + src/helpers/dom/isInDOM.ts | 25 + src/helpers/dom/isInputEmpty.ts | 18 + src/helpers/dom/isSelectionEmpty.ts | 18 + src/helpers/dom/isSendShortcutPressed.ts | 37 ++ src/helpers/dom/placeCaretAtEnd.ts | 35 ++ src/helpers/dom/positionElementByIndex.ts | 27 + src/helpers/dom/reflowScrollableElement.ts | 11 + src/helpers/dom/replaceContent.ts | 20 + src/helpers/dom/setInnerHTML.ts | 10 + src/helpers/dom/toggleDisability.ts | 15 + src/helpers/dom/whichChild.ts | 16 + src/helpers/fastSmoothScroll.ts | 2 +- src/helpers/fillPropertyValue.ts | 17 + src/helpers/generatePathData.ts | 64 ++ src/helpers/sequentialDom.ts | 2 +- src/index.ts | 3 + src/lib/appManagers/appDialogsManager.ts | 4 +- src/lib/appManagers/appImManager.ts | 7 +- src/lib/appManagers/appMessagesManager.ts | 9 +- src/lib/appManagers/appPhotosManager.ts | 2 +- src/lib/appManagers/appProfileManager.ts | 2 +- src/lib/mediaPlayer.ts | 2 +- src/lib/rootScope.ts | 14 +- src/pages/pageAuthCode.ts | 2 +- src/pages/pageIm.ts | 2 +- src/pages/pagePassword.ts | 6 +- src/pages/pageSignIn.ts | 5 +- src/pages/pageSignUp.ts | 3 +- src/pages/pagesManager.ts | 2 +- 111 files changed, 1048 insertions(+), 732 deletions(-) create mode 100644 src/helpers/calcImageInBox.ts create mode 100644 src/helpers/dom/blurActiveElement.ts create mode 100644 src/helpers/dom/canFocus.ts create mode 100644 src/helpers/dom/cancelEvent.ts create mode 100644 src/helpers/dom/cancelSelection.ts create mode 100644 src/helpers/dom/clickEvent.ts create mode 100644 src/helpers/dom/disableTransition.ts create mode 100644 src/helpers/dom/getRichValue.ts create mode 100644 src/helpers/dom/getSelectedNodes.ts create mode 100644 src/helpers/dom/getSelectedText.ts create mode 100644 src/helpers/dom/handleScrollSideEvent.ts create mode 100644 src/helpers/dom/htmlToDocumentFragment.ts create mode 100644 src/helpers/dom/htmlToSpan.ts create mode 100644 src/helpers/dom/isInDOM.ts create mode 100644 src/helpers/dom/isInputEmpty.ts create mode 100644 src/helpers/dom/isSelectionEmpty.ts create mode 100644 src/helpers/dom/isSendShortcutPressed.ts create mode 100644 src/helpers/dom/placeCaretAtEnd.ts create mode 100644 src/helpers/dom/positionElementByIndex.ts create mode 100644 src/helpers/dom/reflowScrollableElement.ts create mode 100644 src/helpers/dom/replaceContent.ts create mode 100644 src/helpers/dom/setInnerHTML.ts create mode 100644 src/helpers/dom/toggleDisability.ts create mode 100644 src/helpers/dom/whichChild.ts create mode 100644 src/helpers/fillPropertyValue.ts create mode 100644 src/helpers/generatePathData.ts diff --git a/src/components/animationIntersector.ts b/src/components/animationIntersector.ts index cdc8886c..5405d76a 100644 --- a/src/components/animationIntersector.ts +++ b/src/components/animationIntersector.ts @@ -4,11 +4,11 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { isInDOM } from "../helpers/dom"; import { RLottiePlayer } from "../lib/lottieLoader"; import rootScope from "../lib/rootScope"; import { isSafari } from "../helpers/userAgent"; import { MOUNT_CLASS_TO } from "../config/debug"; +import isInDOM from "../helpers/dom/isInDOM"; export interface AnimationItem { el: HTMLElement, diff --git a/src/components/appMediaViewer.ts b/src/components/appMediaViewer.ts index c0e065e9..f9739b3a 100644 --- a/src/components/appMediaViewer.ts +++ b/src/components/appMediaViewer.ts @@ -17,7 +17,6 @@ import { logger } from "../lib/logger"; import VideoPlayer from "../lib/mediaPlayer"; import { RichTextProcessor } from "../lib/richtextprocessor"; import rootScope from "../lib/rootScope"; -import { cancelEvent, fillPropertyValue, generatePathData } from "../helpers/dom"; import animationIntersector from "./animationIntersector"; import appMediaPlaybackController from "./appMediaPlaybackController"; import AvatarElement from "./avatar"; @@ -42,6 +41,9 @@ import renderImageFromUrl from "../helpers/dom/renderImageFromUrl"; import findUpAsChild from "../helpers/dom/findUpAsChild"; import getVisibleRect from "../helpers/dom/getVisibleRect"; import appDownloadManager from "../lib/appManagers/appDownloadManager"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import fillPropertyValue from "../helpers/fillPropertyValue"; +import generatePathData from "../helpers/generatePathData"; // TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию // TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода) diff --git a/src/components/appNavigationController.ts b/src/components/appNavigationController.ts index 49df4312..88822c0d 100644 --- a/src/components/appNavigationController.ts +++ b/src/components/appNavigationController.ts @@ -6,9 +6,10 @@ import { MOUNT_CLASS_TO } from "../config/debug"; import { isMobileSafari } from "../helpers/userAgent"; -import { blurActiveElement, cancelEvent } from "../helpers/dom"; import { logger } from "../lib/logger"; import { doubleRaf } from "../helpers/schedulers"; +import blurActiveElement from "../helpers/dom/blurActiveElement"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; export type NavigationItem = { type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' | 'esg' | 'multiselect' | 'input-helper' | 'markup' | 'global-search', diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index 0193ad3e..71894da1 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -5,11 +5,10 @@ */ import { formatDateAccordingToToday, months } from "../helpers/date"; -import { positionElementByIndex } from "../helpers/dom"; import { copy, getObjectKeysAndSort, safeAssign } from "../helpers/object"; import { escapeRegExp, limitSymbols } from "../helpers/string"; import appChatsManager from "../lib/appManagers/appChatsManager"; -import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager"; +import appDialogsManager from "../lib/appManagers/appDialogsManager"; import appMessagesManager, { MyInputMessagesFilter, MyMessage } from "../lib/appManagers/appMessagesManager"; import appPeersManager from "../lib/appManagers/appPeersManager"; import appPhotosManager from "../lib/appManagers/appPhotosManager"; @@ -39,6 +38,7 @@ import findUpTag from "../helpers/dom/findUpTag"; import appSidebarRight from "./sidebarRight"; import mediaSizes from "../helpers/mediaSizes"; import appImManager from "../lib/appManagers/appImManager"; +import positionElementByIndex from "../helpers/dom/positionElementByIndex"; //const testScroll = false; diff --git a/src/components/appSelectPeers.ts b/src/components/appSelectPeers.ts index 0530600e..817a21bb 100644 --- a/src/components/appSelectPeers.ts +++ b/src/components/appSelectPeers.ts @@ -10,7 +10,6 @@ import appMessagesManager, { Dialog } from "../lib/appManagers/appMessagesManage import appPhotosManager from "../lib/appManagers/appPhotosManager"; import appUsersManager from "../lib/appManagers/appUsersManager"; import rootScope from "../lib/rootScope"; -import { cancelEvent, replaceContent } from "../helpers/dom"; import Scrollable from "./scrollable"; import { FocusDirection } from "../helpers/fastSmoothScroll"; import CheckboxField from "./checkboxField"; @@ -20,6 +19,8 @@ import { i18n, LangPackKey, _i18n } from "../lib/langPack"; import findUpAttribute from "../helpers/dom/findUpAttribute"; import findUpClassName from "../helpers/dom/findUpClassName"; import PeerTitle from "./peerTitle"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import replaceContent from "../helpers/dom/replaceContent"; type PeerType = 'contacts' | 'dialogs' | 'channelParticipants'; diff --git a/src/components/audio.ts b/src/components/audio.ts index 83d311c0..33efe143 100644 --- a/src/components/audio.ts +++ b/src/components/audio.ts @@ -16,9 +16,10 @@ import { isSafari } from "../helpers/userAgent"; import appMessagesManager from "../lib/appManagers/appMessagesManager"; import rootScope from "../lib/rootScope"; import './middleEllipsis'; -import { attachClickEvent, cancelEvent, detachClickEvent } from "../helpers/dom"; import { SearchSuperContext } from "./appSearchSuper."; import { formatDateAccordingToToday } from "../helpers/date"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { attachClickEvent, detachClickEvent } from "../helpers/dom/clickEvent"; rootScope.on('messages_media_read', e => { const {mids, peerId} = e; diff --git a/src/components/avatar.ts b/src/components/avatar.ts index 035c3e3d..bc674d09 100644 --- a/src/components/avatar.ts +++ b/src/components/avatar.ts @@ -7,12 +7,13 @@ import appMessagesManager from "../lib/appManagers/appMessagesManager"; import appProfileManager from "../lib/appManagers/appProfileManager"; import rootScope from "../lib/rootScope"; -import { attachClickEvent, cancelEvent } from "../helpers/dom"; import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer"; import { Message } from "../layer"; import appPeersManager from "../lib/appManagers/appPeersManager"; import appPhotosManager from "../lib/appManagers/appPhotosManager"; import type { LazyLoadQueueIntersector } from "./lazyLoadQueue"; +import { attachClickEvent } from "../helpers/dom/clickEvent"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; const onAvatarUpdate = (peerId: number) => { appProfileManager.removeFromAvatarsCache(peerId); diff --git a/src/components/buttonMenu.ts b/src/components/buttonMenu.ts index c4525726..ecb53c47 100644 --- a/src/components/buttonMenu.ts +++ b/src/components/buttonMenu.ts @@ -4,7 +4,8 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { attachClickEvent, AttachClickOptions, cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { AttachClickOptions, attachClickEvent, CLICK_EVENT_NAME } from "../helpers/dom/clickEvent"; import ListenerSetter from "../helpers/listenerSetter"; import { i18n, LangPackKey } from "../lib/langPack"; import CheckboxField from "./checkboxField"; diff --git a/src/components/buttonMenuToggle.ts b/src/components/buttonMenuToggle.ts index 6911c415..ca37fbd1 100644 --- a/src/components/buttonMenuToggle.ts +++ b/src/components/buttonMenuToggle.ts @@ -4,7 +4,8 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { AttachClickOptions, cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { AttachClickOptions, CLICK_EVENT_NAME } from "../helpers/dom/clickEvent"; import ListenerSetter from "../helpers/listenerSetter"; import ButtonIcon from "./buttonIcon"; import ButtonMenu, { ButtonMenuItemOptions } from "./buttonMenu"; diff --git a/src/components/chat/audio.ts b/src/components/chat/audio.ts index 7f115e07..0fe9ac5a 100644 --- a/src/components/chat/audio.ts +++ b/src/components/chat/audio.ts @@ -9,12 +9,13 @@ import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; import type ChatTopbar from "./topbar"; import { RichTextProcessor } from "../../lib/richtextprocessor"; import rootScope from "../../lib/rootScope"; -import { attachClickEvent, cancelEvent } from "../../helpers/dom"; import appMediaPlaybackController from "../appMediaPlaybackController"; import DivAndCaption from "../divAndCaption"; import { formatDate } from "../wrappers"; import PinnedContainer from "./pinnedContainer"; import Chat from "./chat"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; export default class ChatAudio extends PinnedContainer { private toggleEl: HTMLElement; diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 4e923cb6..67f5f5b1 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -5,7 +5,7 @@ */ import type { AppImManager } from "../../lib/appManagers/appImManager"; -import type { AppMessagesManager, HistoryResult, MyMessage } from "../../lib/appManagers/appMessagesManager"; +import type { AppMessagesManager, HistoryResult, HistoryStorage, MyMessage } from "../../lib/appManagers/appMessagesManager"; import type { AppStickersManager } from "../../lib/appManagers/appStickersManager"; import type { AppUsersManager } from "../../lib/appManagers/appUsersManager"; import type { AppInlineBotsManager } from "../../lib/appManagers/appInlineBotsManager"; @@ -15,7 +15,6 @@ import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; import type sessionStorage from '../../lib/sessionStorage'; import type Chat from "./chat"; import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager"; -import { cancelEvent, whichChild, attachClickEvent, positionElementByIndex, reflowScrollableElement, replaceContent, htmlToDocumentFragment, setInnerHTML } from "../../helpers/dom"; import { getObjectKeysAndSort } from "../../helpers/object"; import { isTouchSupported } from "../../helpers/touchSupport"; import { logger } from "../../lib/logger"; @@ -60,6 +59,14 @@ import findUpTag from "../../helpers/dom/findUpTag"; import { toast } from "../toast"; import { getElementByPoint } from "../../helpers/dom/getElementByPoint"; import { getMiddleware } from "../../helpers/middleware"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment"; +import positionElementByIndex from "../../helpers/dom/positionElementByIndex"; +import reflowScrollableElement from "../../helpers/dom/reflowScrollableElement"; +import replaceContent from "../../helpers/dom/replaceContent"; +import setInnerHTML from "../../helpers/dom/setInnerHTML"; +import whichChild from "../../helpers/dom/whichChild"; const USE_MEDIA_TAILS = false; const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */]; @@ -97,7 +104,9 @@ export default class ChatBubbles { private stickyIntersector: StickyIntersector; private unreadedObserver: IntersectionObserver; - private unreaded: number[] = []; + private unreaded: Map = new Map(); + private unreadedSeen: Set = new Set(); + private readPromise: Promise; public bubbleGroups: BubbleGroups; @@ -131,6 +140,7 @@ export default class ChatBubbles { private needReflowScroll: boolean; private fetchNewPromise: Promise; + private historyStorage: HistoryStorage; constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager, private storage: typeof sessionStorage) { //this.chat.log.error('Bubbles construction'); @@ -492,48 +502,13 @@ export default class ChatBubbles { }); this.unreadedObserver = new IntersectionObserver((entries) => { - if(this.chat.appImManager.offline) { // ! but you can scroll the page without triggering 'focus', need something now - return; - } - - const readed: number[] = []; - entries.forEach(entry => { if(entry.isIntersecting) { const target = entry.target as HTMLElement; - const mid = +target.dataset.mid; - readed.push(mid); - this.unreadedObserver.unobserve(target); + const mid = this.unreaded.get(target as HTMLElement); + this.onUnreadedInViewport(target, mid); } }); - - if(readed.length) { - let maxId = Math.max(...readed); - - if(this.scrollable.loadedAll.bottom) { - const bubblesMaxId = Math.max(...Object.keys(this.bubbles).map(i => +i)); - if(maxId >= bubblesMaxId) { - maxId = this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId).maxId || maxId; - } - } - - let length = readed.length; - for(let i = this.unreaded.length - 1; i >= 0; --i) { - if(this.unreaded[i] <= maxId) { - length++; - this.unreaded.splice(i, 1); - } - } - - if(DEBUG) { - this.log('will readHistory by ids:', maxId, length); - } - - /* false && */ this.appMessagesManager.readHistory(this.peerId, maxId, this.chat.threadId).catch((err: any) => { - this.log.error('readHistory err:', err); - this.appMessagesManager.readHistory(this.peerId, maxId, this.chat.threadId); - }); - } }); if('ResizeObserver' in window) { @@ -635,6 +610,55 @@ export default class ChatBubbles { } } + private onUnreadedInViewport(target: HTMLElement, mid: number) { + this.unreadedSeen.add(mid); + this.unreadedObserver.unobserve(target); + this.unreaded.delete(target); + this.readUnreaded(); + } + + private readUnreaded() { + if(this.readPromise) return; + + const middleware = this.getMiddleware(); + this.readPromise = rootScope.idle.focusPromise.then(() => { + if(!middleware()) return; + let maxId = Math.max(...Array.from(this.unreadedSeen)); + + // ? if message with maxId is not rendered ? + if(this.scrollable.loadedAll.bottom) { + const bubblesMaxId = Math.max(...Object.keys(this.bubbles).map(i => +i)); + if(maxId >= bubblesMaxId) { + maxId = Math.max(this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId).maxId || 0, maxId); + } + } + + this.unreaded.forEach((mid, target) => { + if(mid <= maxId) { + this.onUnreadedInViewport(target, mid); + } + }); + + this.unreadedSeen.clear(); + + if(DEBUG) { + this.log('will readHistory by maxId:', maxId); + } + + return this.appMessagesManager.readHistory(this.peerId, maxId, this.chat.threadId).catch((err: any) => { + this.log.error('readHistory err:', err); + this.appMessagesManager.readHistory(this.peerId, maxId, this.chat.threadId); + }).finally(() => { + if(!middleware()) return; + this.readPromise = undefined; + + if(this.unreadedSeen.size) { + this.readUnreaded(); + } + }); + }); + } + public constructPinnedHelpers() { this.listenerSetter.add(rootScope, 'peer_pinned_messages', (e) => { const {peerId, mids, pinned} = e; @@ -1170,6 +1194,7 @@ export default class ChatBubbles { this.bubbleGroups.removeBubble(bubble); if(this.unreadedObserver) { this.unreadedObserver.unobserve(bubble); + this.unreaded.delete(bubble); } //this.unreaded.findAndSplice(mid => mid === id); bubble.remove(); @@ -1409,7 +1434,9 @@ export default class ChatBubbles { if(this.unreadedObserver) { this.unreadedObserver.disconnect(); - this.unreaded.length = 0; + this.unreaded.clear(); + this.unreadedSeen.clear(); + this.readPromise = undefined; } this.loadedTopTimes = this.loadedBottomTimes = 0; @@ -1435,8 +1462,8 @@ export default class ChatBubbles { 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; + this.historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId); + let topMessage = this.chat.type === 'pinned' ? this.appMessagesManager.pinnedMessages[peerId].maxId : this.historyStorage.maxId ?? 0; const isTarget = lastMsgId !== undefined; // * this one will fix topMessage for null message in history (e.g. channel comments with only 1 comment and it is a topMessage) @@ -1491,7 +1518,7 @@ export default class ChatBubbles { } if(DEBUG) { - this.log('setPeer peerId:', this.peerId, historyStorage, lastMsgId, topMessage); + this.log('setPeer peerId:', this.peerId, this.historyStorage, lastMsgId, topMessage); } // add last message, bc in getHistory will load < max_id @@ -1666,7 +1693,11 @@ export default class ChatBubbles { }); }; - f(); + if(samePeer) { + setTimeout(f, 30e3); + } else { + f(); + } } }); } @@ -1674,7 +1705,7 @@ export default class ChatBubbles { this.log('scrolledAllDown:', this.scrollable.loadedAll.bottom); //if(!this.unreaded.length && dialog) { // lol - if(this.scrollable.loadedAll.bottom && topMessage) { // lol + if(this.scrollable.loadedAll.bottom && topMessage && !this.unreaded.size) { // lol this.onScrolledAllDown(); } @@ -1874,13 +1905,12 @@ export default class ChatBubbles { contentWrapper.appendChild(bubbleContainer); bubble.appendChild(contentWrapper); - if(!our && !message.pFlags.out) { + if(!our && !message.pFlags.out && this.unreadedObserver) { //this.log('not our message', message, message.pFlags.unread); - if(message.pFlags.unread && this.unreadedObserver) { + const isUnread = message.pFlags.unread || (this.historyStorage.readMaxId !== undefined && this.historyStorage.readMaxId < message.mid); + if(isUnread) { this.unreadedObserver.observe(bubble); - if(!this.unreaded.indexOf(message.mid)) { - this.unreaded.push(message.mid); - } + this.unreaded.set(bubble, message.mid); } } } else { diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index 8d862f22..feb00921 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -10,7 +10,6 @@ import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; import type { AppPollsManager, Poll } from "../../lib/appManagers/appPollsManager"; import type Chat from "./chat"; import { isTouchSupported } from "../../helpers/touchSupport"; -import { attachClickEvent, cancelEvent, cancelSelection, isSelectionEmpty } from "../../helpers/dom"; import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu"; import { attachContextMenuListener, openBtnMenu, positionMenu } from "../misc"; import PopupDeleteMessages from "../popups/deleteMessages"; @@ -21,6 +20,10 @@ import PopupSendNow from "../popups/sendNow"; import { toast } from "../toast"; import I18n, { LangPackKey } from "../../lib/langPack"; import findUpClassName from "../../helpers/dom/findUpClassName"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import cancelSelection from "../../helpers/dom/cancelSelection"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import isSelectionEmpty from "../../helpers/dom/isSelectionEmpty"; export default class ChatContextMenu { private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true})[]; diff --git a/src/components/chat/dragAndDrop.ts b/src/components/chat/dragAndDrop.ts index 85feec49..0714489d 100644 --- a/src/components/chat/dragAndDrop.ts +++ b/src/components/chat/dragAndDrop.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { generatePathData } from "../../helpers/dom"; +import generatePathData from "../../helpers/generatePathData"; import { i18n, LangPackKey } from "../../lib/langPack"; export default class ChatDragAndDrop { @@ -90,4 +90,4 @@ export default class ChatDragAndDrop { const d = generatePathData(pos, pos, sizeX, sizeY, radius, radius, radius, radius); this.path.setAttributeNS(null, 'd', d); } -} \ No newline at end of file +} diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index a20499ae..7dd5c618 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -20,7 +20,6 @@ import apiManager from "../../lib/mtproto/mtprotoworker"; //import Recorder from '../opus-recorder/dist/recorder.min'; import opusDecodeController from "../../lib/opusDecodeController"; import RichTextProcessor from "../../lib/richtextprocessor"; -import { attachClickEvent, blurActiveElement, cancelEvent, cancelSelection, getRichValue, isInputEmpty, markdownTags, MarkdownType, placeCaretAtEnd, isSendShortcutPressed } from "../../helpers/dom"; import { ButtonMenuItemOptions } from '../buttonMenu'; import emoticonsDropdown from "../emoticonsDropdown"; import PopupCreatePoll from "../popups/createPoll"; @@ -48,6 +47,14 @@ import { i18n } from '../../lib/langPack'; import { generateTail } from './bubbles'; import findUpClassName from '../../helpers/dom/findUpClassName'; import ButtonCorner from '../buttonCorner'; +import blurActiveElement from '../../helpers/dom/blurActiveElement'; +import { cancelEvent } from '../../helpers/dom/cancelEvent'; +import cancelSelection from '../../helpers/dom/cancelSelection'; +import { attachClickEvent } from '../../helpers/dom/clickEvent'; +import getRichValue, { MarkdownType, markdownTags } from '../../helpers/dom/getRichValue'; +import isInputEmpty from '../../helpers/dom/isInputEmpty'; +import isSendShortcutPressed from '../../helpers/dom/isSendShortcutPressed'; +import placeCaretAtEnd from '../../helpers/dom/placeCaretAtEnd'; const RECORD_MIN_TIME = 500; const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.'; diff --git a/src/components/chat/markupTooltip.ts b/src/components/chat/markupTooltip.ts index 0f541866..56f2de90 100644 --- a/src/components/chat/markupTooltip.ts +++ b/src/components/chat/markupTooltip.ts @@ -5,7 +5,6 @@ */ import type { AppImManager } from "../../lib/appManagers/appImManager"; -import { MarkdownType, cancelEvent, getSelectedNodes, markdownTags, attachClickEvent, isSelectionEmpty } from "../../helpers/dom"; import RichTextProcessor from "../../lib/richtextprocessor"; import ButtonIcon from "../buttonIcon"; import { clamp } from "../../helpers/number"; @@ -13,6 +12,11 @@ import { isTouchSupported } from "../../helpers/touchSupport"; import { isApple, isMobile } from "../../helpers/userAgent"; import appNavigationController from "../appNavigationController"; import { _i18n } from "../../lib/langPack"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import { MarkdownType, markdownTags } from "../../helpers/dom/getRichValue"; +import getSelectedNodes from "../../helpers/dom/getSelectedNodes"; +import isSelectionEmpty from "../../helpers/dom/isSelectionEmpty"; //import { logger } from "../../lib/logger"; export default class MarkupTooltip { diff --git a/src/components/chat/pinnedContainer.ts b/src/components/chat/pinnedContainer.ts index 33e8e5c7..36350f8c 100644 --- a/src/components/chat/pinnedContainer.ts +++ b/src/components/chat/pinnedContainer.ts @@ -7,10 +7,11 @@ import type Chat from "./chat"; import type ChatTopbar from "./topbar"; import mediaSizes from "../../helpers/mediaSizes"; -import { attachClickEvent, cancelEvent } from "../../helpers/dom"; import DivAndCaption from "../divAndCaption"; import { ripple } from "../ripple"; import ListenerSetter from "../../helpers/listenerSetter"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; //const classNames: string[] = []; const classNames: string[] = ['is-pinned-message-shown', 'is-pinned-audio-shown']; diff --git a/src/components/chat/pinnedMessage.ts b/src/components/chat/pinnedMessage.ts index fc1b66d3..2f8dfd6e 100644 --- a/src/components/chat/pinnedMessage.ts +++ b/src/components/chat/pinnedMessage.ts @@ -12,13 +12,15 @@ import PinnedContainer from "./pinnedContainer"; import PinnedMessageBorder from "./pinnedMessageBorder"; import ReplyContainer, { wrapReplyDivAndCaption } from "./replyContainer"; import rootScope from "../../lib/rootScope"; -import { attachClickEvent, cancelEvent, handleScrollSideEvent } from "../../helpers/dom"; import Chat from "./chat"; import ListenerSetter from "../../helpers/listenerSetter"; import ButtonIcon from "../buttonIcon"; import { debounce } from "../../helpers/schedulers"; import { getHeavyAnimationPromise } from "../../hooks/useHeavyAnimationCheck"; import { i18n } from "../../lib/langPack"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import handleScrollSideEvent from "../../helpers/dom/handleScrollSideEvent"; class AnimatedSuper { static DURATION = 200; diff --git a/src/components/chat/search.ts b/src/components/chat/search.ts index b2155d54..3cd20ced 100644 --- a/src/components/chat/search.ts +++ b/src/components/chat/search.ts @@ -5,13 +5,14 @@ */ import type ChatTopbar from "./topbar"; -import { cancelEvent, whichChild } from "../../helpers/dom"; import AppSearch, { SearchGroup } from "../appSearch"; import PopupDatePicker from "../popups/datePicker"; import { ripple } from "../ripple"; import InputSearch from "../inputSearch"; import type Chat from "./chat"; import findUpTag from "../../helpers/dom/findUpTag"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import whichChild from "../../helpers/dom/whichChild"; export default class ChatSearch { private element: HTMLElement; diff --git a/src/components/chat/selection.ts b/src/components/chat/selection.ts index 910f7f3d..1e19c5d0 100644 --- a/src/components/chat/selection.ts +++ b/src/components/chat/selection.ts @@ -9,7 +9,6 @@ import type ChatBubbles from "./bubbles"; import type ChatInput from "./input"; import type Chat from "./chat"; import { isTouchSupported } from "../../helpers/touchSupport"; -import { blurActiveElement, cancelEvent, cancelSelection, getSelectedText } from "../../helpers/dom"; import Button from "../button"; import ButtonIcon from "../buttonIcon"; import CheckboxField from "../checkboxField"; @@ -23,6 +22,10 @@ import appNavigationController from "../appNavigationController"; import { isMobileSafari } from "../../helpers/userAgent"; import I18n, { i18n, _i18n } from "../../lib/langPack"; import findUpClassName from "../../helpers/dom/findUpClassName"; +import blurActiveElement from "../../helpers/dom/blurActiveElement"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import cancelSelection from "../../helpers/dom/cancelSelection"; +import getSelectedText from "../../helpers/dom/getSelectedText"; const MAX_SELECTION_LENGTH = 100; //const MIN_CLICK_MOVE = 32; // minimum bubble height diff --git a/src/components/chat/sendContextMenu.ts b/src/components/chat/sendContextMenu.ts index 7fa0f22f..7229b957 100644 --- a/src/components/chat/sendContextMenu.ts +++ b/src/components/chat/sendContextMenu.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { cancelEvent } from "../../helpers/dom"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; import ListenerSetter from "../../helpers/listenerSetter"; import rootScope from "../../lib/rootScope"; import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu"; diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index 7c4cbe11..ab10707c 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -10,7 +10,6 @@ import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManage import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; import type { AppSidebarRight } from "../sidebarRight"; import type Chat from "./chat"; -import { cancelEvent, attachClickEvent, blurActiveElement, replaceContent } from "../../helpers/dom"; import mediaSizes, { ScreenSize } from "../../helpers/mediaSizes"; import { isSafari } from "../../helpers/userAgent"; import rootScope from "../../lib/rootScope"; @@ -31,6 +30,9 @@ import AppPrivateSearchTab from "../sidebarRight/tabs/search"; import PeerTitle from "../peerTitle"; import { i18n } from "../../lib/langPack"; import findUpClassName from "../../helpers/dom/findUpClassName"; +import blurActiveElement from "../../helpers/dom/blurActiveElement"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; export default class ChatTopbar { container: HTMLDivElement; diff --git a/src/components/emoticonsDropdown/index.ts b/src/components/emoticonsDropdown/index.ts index 0edebf8e..b1dcc2fe 100644 --- a/src/components/emoticonsDropdown/index.ts +++ b/src/components/emoticonsDropdown/index.ts @@ -8,7 +8,6 @@ import { isTouchSupported } from "../../helpers/touchSupport"; import appChatsManager from "../../lib/appManagers/appChatsManager"; import appImManager from "../../lib/appManagers/appImManager"; import rootScope from "../../lib/rootScope"; -import { attachClickEvent, blurActiveElement, whichChild } from "../../helpers/dom"; import animationIntersector from "../animationIntersector"; import { horizontalMenu } from "../horizontalMenu"; import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue"; @@ -25,6 +24,9 @@ import AppStickersTab from "../sidebarRight/tabs/stickers"; import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpTag from "../../helpers/dom/findUpTag"; import ListenerSetter from "../../helpers/listenerSetter"; +import blurActiveElement from "../../helpers/dom/blurActiveElement"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import whichChild from "../../helpers/dom/whichChild"; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; diff --git a/src/components/gifsMasonry.ts b/src/components/gifsMasonry.ts index c5191357..d7a1af26 100644 --- a/src/components/gifsMasonry.ts +++ b/src/components/gifsMasonry.ts @@ -4,7 +4,6 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { calcImageInBox } from "../helpers/dom"; import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager"; import { wrapVideo } from "./wrappers"; import { LazyLoadQueueRepeat2 } from "./lazyLoadQueue"; @@ -12,6 +11,7 @@ import animationIntersector from "./animationIntersector"; import Scrollable from "./scrollable"; import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise"; import renderImageFromUrl from "../helpers/dom/renderImageFromUrl"; +import calcImageInBox from "../helpers/calcImageInBox"; const width = 400; const maxSingleWidth = width - 100; diff --git a/src/components/horizontalMenu.ts b/src/components/horizontalMenu.ts index 20c811cc..14333206 100644 --- a/src/components/horizontalMenu.ts +++ b/src/components/horizontalMenu.ts @@ -4,13 +4,13 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { whichChild } from "../helpers/dom"; import { TransitionSlider } from "./transition"; import { ScrollableX } from "./scrollable"; import rootScope from "../lib/rootScope"; import { fastRaf } from "../helpers/schedulers"; import { FocusDirection } from "../helpers/fastSmoothScroll"; import findUpAsChild from "../helpers/dom/findUpAsChild"; +import whichChild from "../helpers/dom/whichChild"; 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); diff --git a/src/components/inputField.ts b/src/components/inputField.ts index 5b087eea..7aecf36c 100644 --- a/src/components/inputField.ts +++ b/src/components/inputField.ts @@ -4,9 +4,9 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { getRichValue, isInputEmpty } from "../helpers/dom"; +import getRichValue from "../helpers/dom/getRichValue"; +import isInputEmpty from "../helpers/dom/isInputEmpty"; import { debounce } from "../helpers/schedulers"; -import { checkRTL } from "../helpers/string"; import { i18n, LangPackKey, _i18n } from "../lib/langPack"; import RichTextProcessor from "../lib/richtextprocessor"; diff --git a/src/components/languageChangeButton.ts b/src/components/languageChangeButton.ts index 92bfab3b..9d8aa6de 100644 --- a/src/components/languageChangeButton.ts +++ b/src/components/languageChangeButton.ts @@ -1,4 +1,11 @@ -import { attachClickEvent, cancelEvent } from "../helpers/dom"; +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../helpers/dom/clickEvent"; import { Config, LangPackDifference, LangPackString } from "../layer"; import I18n, { LangPackKey } from "../lib/langPack"; import apiManager from "../lib/mtproto/mtprotoworker"; diff --git a/src/components/misc.ts b/src/components/misc.ts index a46bd3cd..2e1b5928 100644 --- a/src/components/misc.ts +++ b/src/components/misc.ts @@ -6,7 +6,8 @@ import { MOUNT_CLASS_TO } from "../config/debug"; import Countries, { Country, PhoneCodesMain } from "../countries"; -import { cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { CLICK_EVENT_NAME } from "../helpers/dom/clickEvent"; import ListenerSetter from "../helpers/listenerSetter"; import mediaSizes from "../helpers/mediaSizes"; import { isTouchSupported } from "../helpers/touchSupport"; diff --git a/src/components/passwordInputField.ts b/src/components/passwordInputField.ts index 1f0e1465..48424e4c 100644 --- a/src/components/passwordInputField.ts +++ b/src/components/passwordInputField.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { cancelEvent } from "../helpers/dom"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; import InputField, { InputFieldOptions } from "./inputField"; export default class PasswordInputField extends InputField { diff --git a/src/components/peerTitle.ts b/src/components/peerTitle.ts index a34ad289..9a17812b 100644 --- a/src/components/peerTitle.ts +++ b/src/components/peerTitle.ts @@ -7,8 +7,8 @@ import { MOUNT_CLASS_TO } from "../config/debug"; import appPeersManager from "../lib/appManagers/appPeersManager"; import rootScope from "../lib/rootScope"; -import { replaceContent } from "../helpers/dom"; import { i18n } from "../lib/langPack"; +import replaceContent from "../helpers/dom/replaceContent"; export type PeerTitleOptions = { peerId: number, diff --git a/src/components/poll.ts b/src/components/poll.ts index 5eadb387..c6d281ec 100644 --- a/src/components/poll.ts +++ b/src/components/poll.ts @@ -11,7 +11,6 @@ import appPollsManager, { Poll, PollResults } from "../lib/appManagers/appPollsM import serverTimeManager from "../lib/mtproto/serverTimeManager"; import { RichTextProcessor } from "../lib/richtextprocessor"; import rootScope from "../lib/rootScope"; -import { attachClickEvent, cancelEvent, detachClickEvent, replaceContent } from "../helpers/dom"; import { ripple } from "./ripple"; import appSidebarRight from "./sidebarRight"; import AppPollResultsTab from "./sidebarRight/tabs/pollResults"; @@ -19,6 +18,9 @@ import { i18n, LangPackKey } from "../lib/langPack"; import { fastRaf } from "../helpers/schedulers"; import SetTransition from "./singleTransition"; import findUpClassName from "../helpers/dom/findUpClassName"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { attachClickEvent, detachClickEvent } from "../helpers/dom/clickEvent"; +import replaceContent from "../helpers/dom/replaceContent"; let lineTotalLength = 0; //const tailLength = 9; diff --git a/src/components/popups/createPoll.ts b/src/components/popups/createPoll.ts index 362cd9ef..292c630a 100644 --- a/src/components/popups/createPoll.ts +++ b/src/components/popups/createPoll.ts @@ -7,7 +7,6 @@ import type { Poll } from "../../lib/appManagers/appPollsManager"; import type Chat from "../chat/chat"; import PopupElement from "."; -import { cancelEvent, getRichValue, isInputEmpty, whichChild } from "../../helpers/dom"; import CheckboxField from "../checkboxField"; import InputField from "../inputField"; import RadioField from "../radioField"; @@ -16,6 +15,10 @@ import SendContextMenu from "../chat/sendContextMenu"; import { MessageEntity } from "../../layer"; import I18n, { _i18n, i18n } from "../../lib/langPack"; import findUpTag from "../../helpers/dom/findUpTag"; +import { cancelEvent } from "../../helpers/dom/cancelEvent"; +import getRichValue from "../../helpers/dom/getRichValue"; +import isInputEmpty from "../../helpers/dom/isInputEmpty"; +import whichChild from "../../helpers/dom/whichChild"; const MAX_LENGTH_QUESTION = 255; const MAX_LENGTH_OPTION = 100; diff --git a/src/components/popups/index.ts b/src/components/popups/index.ts index a9238362..78ddadcc 100644 --- a/src/components/popups/index.ts +++ b/src/components/popups/index.ts @@ -5,12 +5,12 @@ */ import rootScope from "../../lib/rootScope"; -import { blurActiveElement } from "../../helpers/dom"; import { ripple } from "../ripple"; import animationIntersector from "../animationIntersector"; import appNavigationController, { NavigationItem } from "../appNavigationController"; import { i18n, LangPackKey } from "../../lib/langPack"; import findUpClassName from "../../helpers/dom/findUpClassName"; +import blurActiveElement from "../../helpers/dom/blurActiveElement"; export type PopupButton = { text?: string, diff --git a/src/components/popups/newMedia.ts b/src/components/popups/newMedia.ts index 031cb87f..8cc778f0 100644 --- a/src/components/popups/newMedia.ts +++ b/src/components/popups/newMedia.ts @@ -5,7 +5,6 @@ */ import type Chat from "../chat/chat"; -import { calcImageInBox, placeCaretAtEnd, isSendShortcutPressed } from "../../helpers/dom"; import InputField from "../inputField"; import PopupElement from "."; import Scrollable from "../scrollable"; @@ -17,6 +16,9 @@ import { createPosterFromVideo, onVideoLoad } from "../../helpers/files"; import { MyDocument } from "../../lib/appManagers/appDocsManager"; import I18n, { i18n, LangPackKey } from "../../lib/langPack"; import appDownloadManager from "../../lib/appManagers/appDownloadManager"; +import calcImageInBox from "../../helpers/calcImageInBox"; +import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed"; +import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd"; type SendFileParams = Partial<{ file: File, diff --git a/src/components/popups/stickers.ts b/src/components/popups/stickers.ts index 6ef45ce7..0758156f 100644 --- a/src/components/popups/stickers.ts +++ b/src/components/popups/stickers.ts @@ -12,13 +12,13 @@ import { wrapSticker } from "../wrappers"; import LazyLoadQueue from "../lazyLoadQueue"; import { putPreloader } from "../misc"; import animationIntersector from "../animationIntersector"; -import { toggleDisability } from "../../helpers/dom"; import appImManager from "../../lib/appManagers/appImManager"; import { StickerSet } from "../../layer"; import mediaSizes from "../../helpers/mediaSizes"; import { i18n } from "../../lib/langPack"; import Button from "../button"; import findUpClassName from "../../helpers/dom/findUpClassName"; +import toggleDisability from "../../helpers/dom/toggleDisability"; const ANIMATION_GROUP = 'STICKERS-POPUP'; diff --git a/src/components/preloader.ts b/src/components/preloader.ts index 848afbbe..c45ab86e 100644 --- a/src/components/preloader.ts +++ b/src/components/preloader.ts @@ -4,11 +4,13 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { isInDOM, cancelEvent, attachClickEvent } from "../helpers/dom"; import { CancellablePromise } from "../helpers/cancellablePromise"; import SetTransition from "./singleTransition"; import { fastRaf } from "../helpers/schedulers"; import { safeAssign } from "../helpers/object"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../helpers/dom/clickEvent"; +import isInDOM from "../helpers/dom/isInDOM"; const TRANSITION_TIME = 200; diff --git a/src/components/row.ts b/src/components/row.ts index d234dc90..9c36ca67 100644 --- a/src/components/row.ts +++ b/src/components/row.ts @@ -10,7 +10,7 @@ import { ripple } from "./ripple"; import { SliderSuperTab } from "./slider"; import RadioForm from "./radioForm"; import { i18n, LangPackKey } from "../lib/langPack"; -import { replaceContent } from "../helpers/dom"; +import replaceContent from "../helpers/dom/replaceContent"; export default class Row { public container: HTMLElement; diff --git a/src/components/scrollable.ts b/src/components/scrollable.ts index 69a2ed3d..fedcc91f 100644 --- a/src/components/scrollable.ts +++ b/src/components/scrollable.ts @@ -8,7 +8,7 @@ import { isTouchSupported } from "../helpers/touchSupport"; import { logger, LogTypes } from "../lib/logger"; import fastSmoothScroll, { FocusDirection } from "../helpers/fastSmoothScroll"; import useHeavyAnimationCheck from "../hooks/useHeavyAnimationCheck"; -import { cancelEvent } from "../helpers/dom"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; /* var el = $0; var height = 0; diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index c127a3de..6a95bdbe 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -34,7 +34,6 @@ import appNavigationController from "../appNavigationController"; import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpTag from "../../helpers/dom/findUpTag"; import PeerTitle from "../peerTitle"; -import { replaceContent } from "../../helpers/dom"; import App from "../../config/app"; import ButtonMenuToggle from "../buttonMenuToggle"; @@ -607,3 +606,7 @@ export const generateSection = (appendTo: Scrollable, name?: LangPackKey, captio const appSidebarLeft = new AppSidebarLeft(); MOUNT_CLASS_TO.appSidebarLeft = appSidebarLeft; export default appSidebarLeft; +function replaceContent(div: HTMLDivElement, title: HTMLElement) { + throw new Error("Function not implemented."); +} + diff --git a/src/components/sidebarLeft/tabs/2fa/email.ts b/src/components/sidebarLeft/tabs/2fa/email.ts index 48638ecb..a4358a46 100644 --- a/src/components/sidebarLeft/tabs/2fa/email.ts +++ b/src/components/sidebarLeft/tabs/2fa/email.ts @@ -11,13 +11,15 @@ import Button from "../../../button"; import { SliderSuperTab } from "../../../slider"; import { wrapSticker } from "../../../wrappers"; import InputField from "../../../inputField"; -import { attachClickEvent, cancelEvent, canFocus } from "../../../../helpers/dom"; import { putPreloader } from "../../../misc"; import passwordManager from "../../../../lib/mtproto/passwordManager"; import AppTwoStepVerificationSetTab from "./passwordSet"; import AppTwoStepVerificationEmailConfirmationTab from "./emailConfirmation"; import RichTextProcessor from "../../../../lib/richtextprocessor"; import PopupPeer from "../../../popups/peer"; +import { cancelEvent } from "../../../../helpers/dom/cancelEvent"; +import { canFocus } from "../../../../helpers/dom/canFocus"; +import { attachClickEvent } from "../../../../helpers/dom/clickEvent"; export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { public inputField: InputField; diff --git a/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts b/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts index 66c1fc93..f99d9274 100644 --- a/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts +++ b/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts @@ -10,13 +10,16 @@ import appStickersManager from "../../../../lib/appManagers/appStickersManager"; import Button from "../../../button"; import { SliderSuperTab } from "../../../slider"; import { wrapSticker } from "../../../wrappers"; -import { attachClickEvent, canFocus, replaceContent, toggleDisability } from "../../../../helpers/dom"; import passwordManager from "../../../../lib/mtproto/passwordManager"; import AppTwoStepVerificationSetTab from "./passwordSet"; import CodeInputField from "../../../codeInputField"; import AppTwoStepVerificationEmailTab from "./email"; import { putPreloader } from "../../../misc"; import { i18n, _i18n } from "../../../../lib/langPack"; +import { canFocus } from "../../../../helpers/dom/canFocus"; +import { attachClickEvent } from "../../../../helpers/dom/clickEvent"; +import replaceContent from "../../../../helpers/dom/replaceContent"; +import toggleDisability from "../../../../helpers/dom/toggleDisability"; export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSuperTab { public codeInputField: CodeInputField; diff --git a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts index 0bb22341..3d09f85b 100644 --- a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts @@ -6,7 +6,10 @@ import AppTwoStepVerificationTab from "."; import { SettingSection } from "../.."; -import { attachClickEvent, cancelEvent, canFocus, replaceContent } from "../../../../helpers/dom"; +import { cancelEvent } from "../../../../helpers/dom/cancelEvent"; +import { canFocus } from "../../../../helpers/dom/canFocus"; +import { attachClickEvent } from "../../../../helpers/dom/clickEvent"; +import replaceContent from "../../../../helpers/dom/replaceContent"; import { AccountPassword } from "../../../../layer"; import I18n, { i18n } from "../../../../lib/langPack"; import passwordManager from "../../../../lib/mtproto/passwordManager"; diff --git a/src/components/sidebarLeft/tabs/2fa/hint.ts b/src/components/sidebarLeft/tabs/2fa/hint.ts index 9d7b0786..7ffc0d05 100644 --- a/src/components/sidebarLeft/tabs/2fa/hint.ts +++ b/src/components/sidebarLeft/tabs/2fa/hint.ts @@ -12,9 +12,10 @@ import { SliderSuperTab } from "../../../slider"; import { wrapSticker } from "../../../wrappers"; import InputField from "../../../inputField"; import AppTwoStepVerificationEmailTab from "./email"; -import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { toast } from "../../../toast"; import I18n from "../../../../lib/langPack"; +import { cancelEvent } from "../../../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../../../helpers/dom/clickEvent"; export default class AppTwoStepVerificationHintTab extends SliderSuperTab { public inputField: InputField; diff --git a/src/components/sidebarLeft/tabs/2fa/index.ts b/src/components/sidebarLeft/tabs/2fa/index.ts index 5d2ba1e1..3e80b652 100644 --- a/src/components/sidebarLeft/tabs/2fa/index.ts +++ b/src/components/sidebarLeft/tabs/2fa/index.ts @@ -5,7 +5,7 @@ */ import { SettingSection } from "../.."; -import { attachClickEvent } from "../../../../helpers/dom"; +import { attachClickEvent } from "../../../../helpers/dom/clickEvent"; import { AccountPassword } from "../../../../layer"; import appStickersManager from "../../../../lib/appManagers/appStickersManager"; import { _i18n } from "../../../../lib/langPack"; diff --git a/src/components/sidebarLeft/tabs/2fa/passwordSet.ts b/src/components/sidebarLeft/tabs/2fa/passwordSet.ts index 98e86eee..b37e33bf 100644 --- a/src/components/sidebarLeft/tabs/2fa/passwordSet.ts +++ b/src/components/sidebarLeft/tabs/2fa/passwordSet.ts @@ -5,7 +5,7 @@ */ import { SettingSection } from "../.."; -import { attachClickEvent } from "../../../../helpers/dom"; +import { attachClickEvent } from "../../../../helpers/dom/clickEvent"; import appStickersManager from "../../../../lib/appManagers/appStickersManager"; import Button from "../../../button"; import { SliderSuperTab } from "../../../slider"; diff --git a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts index 7dc5df8f..2eb64d45 100644 --- a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts @@ -5,7 +5,6 @@ */ import { SettingSection } from "../.."; -import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { AccountPassword } from "../../../../layer"; import Button from "../../../button"; import PasswordInputField from "../../../passwordInputField"; @@ -13,6 +12,8 @@ import { SliderSuperTab } from "../../../slider"; import TrackingMonkey from "../../../monkeys/tracking"; import AppTwoStepVerificationHintTab from "./hint"; import { InputState } from "../../../inputField"; +import { cancelEvent } from "../../../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../../../helpers/dom/clickEvent"; export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSuperTab { public state: AccountPassword; diff --git a/src/components/sidebarLeft/tabs/activeSessions.ts b/src/components/sidebarLeft/tabs/activeSessions.ts index aa9367e2..72dd244c 100644 --- a/src/components/sidebarLeft/tabs/activeSessions.ts +++ b/src/components/sidebarLeft/tabs/activeSessions.ts @@ -11,7 +11,6 @@ import Row from "../../row"; import { Authorization } from "../../../layer"; import { formatDateAccordingToToday } from "../../../helpers/date"; import { attachContextMenuListener, openBtnMenu, positionMenu } from "../../misc"; -import { attachClickEvent, toggleDisability } from "../../../helpers/dom"; import ButtonMenu from "../../buttonMenu"; import apiManager from "../../../lib/mtproto/mtprotoworker"; import { toast } from "../../toast"; @@ -19,6 +18,8 @@ import AppPrivacyAndSecurityTab from "./privacyAndSecurity"; import I18n from "../../../lib/langPack"; import PopupPeer from "../../popups/peer"; import findUpClassName from "../../../helpers/dom/findUpClassName"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import toggleDisability from "../../../helpers/dom/toggleDisability"; export default class AppActiveSessionsTab extends SliderSuperTab { public privacyTab: AppPrivacyAndSecurityTab; diff --git a/src/components/sidebarLeft/tabs/background.ts b/src/components/sidebarLeft/tabs/background.ts index aecd2752..3be8f696 100644 --- a/src/components/sidebarLeft/tabs/background.ts +++ b/src/components/sidebarLeft/tabs/background.ts @@ -8,7 +8,7 @@ import { generateSection } from ".."; import { averageColor } from "../../../helpers/averageColor"; import blur from "../../../helpers/blur"; import { deferredPromise } from "../../../helpers/cancellablePromise"; -import { attachClickEvent } from "../../../helpers/dom"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import findUpClassName from "../../../helpers/dom/findUpClassName"; import { requestFile } from "../../../helpers/files"; import highlightningColor from "../../../helpers/highlightningColor"; diff --git a/src/components/sidebarLeft/tabs/backgroundColor.ts b/src/components/sidebarLeft/tabs/backgroundColor.ts index 7e463af9..2026cce9 100644 --- a/src/components/sidebarLeft/tabs/backgroundColor.ts +++ b/src/components/sidebarLeft/tabs/backgroundColor.ts @@ -1,6 +1,5 @@ import { SettingSection } from ".."; import { hexaToRgba } from "../../../helpers/color"; -import { attachClickEvent } from "../../../helpers/dom"; import findUpClassName from "../../../helpers/dom/findUpClassName"; import highlightningColor from "../../../helpers/highlightningColor"; import { throttle } from "../../../helpers/schedulers"; @@ -143,3 +142,7 @@ export default class AppBackgroundColorTab extends SliderSuperTab { return super.onCloseAfterTimeout(); } } +function attachClickEvent(grid: HTMLDivElement, arg1: (e: any) => void, arg2: { listenerSetter: import("../../../helpers/listenerSetter").default; }) { + throw new Error("Function not implemented."); +} + diff --git a/src/components/sidebarLeft/tabs/blockedUsers.ts b/src/components/sidebarLeft/tabs/blockedUsers.ts index 098165fc..b91cf135 100644 --- a/src/components/sidebarLeft/tabs/blockedUsers.ts +++ b/src/components/sidebarLeft/tabs/blockedUsers.ts @@ -7,7 +7,6 @@ import { SliderSuperTab } from "../../slider"; import { SettingSection } from ".."; import { attachContextMenuListener, openBtnMenu, positionMenu } from "../../misc"; -import { attachClickEvent } from "../../../helpers/dom"; import ButtonMenu from "../../buttonMenu"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; @@ -157,3 +156,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab { return super.onCloseAfterTimeout(); } } +function attachClickEvent(btnAdd: HTMLButtonElement, arg1: (e: any) => void, arg2: { listenerSetter: import("../../../helpers/listenerSetter").default; }) { + throw new Error("Function not implemented."); +} + diff --git a/src/components/sidebarLeft/tabs/chatFolders.ts b/src/components/sidebarLeft/tabs/chatFolders.ts index 5197cf3b..7da4ec6c 100644 --- a/src/components/sidebarLeft/tabs/chatFolders.ts +++ b/src/components/sidebarLeft/tabs/chatFolders.ts @@ -7,7 +7,6 @@ import { SliderSuperTab } from "../../slider"; import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader"; import { RichTextProcessor } from "../../../lib/richtextprocessor"; -import { attachClickEvent, cancelEvent, positionElementByIndex } from "../../../helpers/dom"; import { toast } from "../../toast"; import type { MyDialogFilter } from "../../../lib/storages/filters"; import type { DialogFilterSuggested, DialogFilter } from "../../../layer"; @@ -22,6 +21,9 @@ import AppEditFolderTab from "./editFolder"; import Row from "../../row"; import { SettingSection } from ".."; import { i18n, i18n_, LangPackKey, join } from "../../../lib/langPack"; +import { cancelEvent } from "../../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import positionElementByIndex from "../../../helpers/dom/positionElementByIndex"; export default class AppChatFoldersTab extends SliderSuperTab { private createFolderBtn: HTMLElement; diff --git a/src/components/sidebarLeft/tabs/contacts.ts b/src/components/sidebarLeft/tabs/contacts.ts index c3a2625c..560fed32 100644 --- a/src/components/sidebarLeft/tabs/contacts.ts +++ b/src/components/sidebarLeft/tabs/contacts.ts @@ -10,8 +10,8 @@ import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appPhotosManager from "../../../lib/appManagers/appPhotosManager"; import rootScope from "../../../lib/rootScope"; import InputSearch from "../../inputSearch"; -import { canFocus } from "../../../helpers/dom"; import { isMobile } from "../../../helpers/userAgent"; +import { canFocus } from "../../../helpers/dom/canFocus"; // TODO: поиск по людям глобальный, если не нашло в контактах никого diff --git a/src/components/sidebarLeft/tabs/editFolder.ts b/src/components/sidebarLeft/tabs/editFolder.ts index 95e25de6..b776ab5d 100644 --- a/src/components/sidebarLeft/tabs/editFolder.ts +++ b/src/components/sidebarLeft/tabs/editFolder.ts @@ -8,7 +8,6 @@ import { deepEqual, copy } from "../../../helpers/object"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters"; import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader"; -import { ripple } from "../../ripple"; import { SliderSuperTab } from "../../slider"; import { toast } from "../../toast"; import appMessagesManager from "../../../lib/appManagers/appMessagesManager"; diff --git a/src/components/sidebarLeft/tabs/editProfile.ts b/src/components/sidebarLeft/tabs/editProfile.ts index 380305a1..b13af631 100644 --- a/src/components/sidebarLeft/tabs/editProfile.ts +++ b/src/components/sidebarLeft/tabs/editProfile.ts @@ -8,10 +8,10 @@ import appProfileManager from "../../../lib/appManagers/appProfileManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import InputField from "../../inputField"; import { SliderSuperTab } from "../../slider"; -import { attachClickEvent } from "../../../helpers/dom"; import EditPeer from "../../editPeer"; import { UsernameInputField } from "../../usernameInputField"; import { i18n, i18n_ } from "../../../lib/langPack"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; // TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист) diff --git a/src/components/sidebarLeft/tabs/generalSettings.ts b/src/components/sidebarLeft/tabs/generalSettings.ts index 8982c031..a7d8af19 100644 --- a/src/components/sidebarLeft/tabs/generalSettings.ts +++ b/src/components/sidebarLeft/tabs/generalSettings.ts @@ -14,9 +14,9 @@ import appStateManager from "../../../lib/appManagers/appStateManager"; import rootScope from "../../../lib/rootScope"; import { isApple } from "../../../helpers/userAgent"; import Row from "../../row"; -import { attachClickEvent } from "../../../helpers/dom"; import AppBackgroundTab from "./background"; import { LangPackKey, _i18n } from "../../../lib/langPack"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; export class RangeSettingSelector { public container: HTMLDivElement; diff --git a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts index afb29196..70bed3b3 100644 --- a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts +++ b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts @@ -26,7 +26,7 @@ import appUsersManager from "../../../lib/appManagers/appUsersManager"; import rootScope from "../../../lib/rootScope"; import { convertKeyToInputKey } from "../../../helpers/string"; import { i18n, LangPackKey, _i18n } from "../../../lib/langPack"; -import { replaceContent } from "../../../helpers/dom"; +import replaceContent from "../../../helpers/dom/replaceContent"; export default class AppPrivacyAndSecurityTab extends SliderSuperTab { private activeSessionsRow: Row; diff --git a/src/components/sidebarRight/tabs/chatType.ts b/src/components/sidebarRight/tabs/chatType.ts index 7bde27c7..811ab378 100644 --- a/src/components/sidebarRight/tabs/chatType.ts +++ b/src/components/sidebarRight/tabs/chatType.ts @@ -5,7 +5,6 @@ */ import { copyTextToClipboard } from "../../../helpers/clipboard"; -import { attachClickEvent, toggleDisability } from "../../../helpers/dom"; import { randomLong } from "../../../helpers/random"; import { Chat, ChatFull, ExportedChatInvite } from "../../../layer"; import appChatsManager from "../../../lib/appManagers/appChatsManager"; @@ -21,6 +20,8 @@ import { SliderSuperTabEventable } from "../../sliderTab"; import I18n from "../../../lib/langPack"; import PopupPeer from "../../popups/peer"; import ButtonCorner from "../../buttonCorner"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import toggleDisability from "../../../helpers/dom/toggleDisability"; export default class AppChatTypeTab extends SliderSuperTabEventable { public chatId: number; diff --git a/src/components/sidebarRight/tabs/editChat.ts b/src/components/sidebarRight/tabs/editChat.ts index f753f505..eb934b7f 100644 --- a/src/components/sidebarRight/tabs/editChat.ts +++ b/src/components/sidebarRight/tabs/editChat.ts @@ -12,7 +12,6 @@ import Row from "../../row"; import Button from "../../button"; import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager"; import appProfileManager from "../../../lib/appManagers/appProfileManager"; -import { attachClickEvent, toggleDisability } from "../../../helpers/dom"; import { Chat } from "../../../layer"; import AppChatTypeTab from "./chatType"; import rootScope from "../../../lib/rootScope"; @@ -21,6 +20,8 @@ import { i18n, LangPackKey } from "../../../lib/langPack"; import PopupDeleteDialog from "../../popups/deleteDialog"; import { addCancelButton } from "../../popups"; import PopupPeer from "../../popups/peer"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import toggleDisability from "../../../helpers/dom/toggleDisability"; export default class AppEditChatTab extends SliderSuperTab { private chatNameInputField: InputField; diff --git a/src/components/sidebarRight/tabs/editContact.ts b/src/components/sidebarRight/tabs/editContact.ts index 595a3509..deba3f56 100644 --- a/src/components/sidebarRight/tabs/editContact.ts +++ b/src/components/sidebarRight/tabs/editContact.ts @@ -11,7 +11,6 @@ import { SettingSection } from "../../sidebarLeft"; import Row from "../../row"; import CheckboxField from "../../checkboxField"; import Button from "../../button"; -import { attachClickEvent, toggleDisability } from "../../../helpers/dom"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appNotificationsManager from "../../../lib/appManagers/appNotificationsManager"; import PeerTitle from "../../peerTitle"; @@ -21,6 +20,8 @@ import appPeersManager from "../../../lib/appManagers/appPeersManager"; import PopupPeer from "../../popups/peer"; import { addCancelButton } from "../../popups"; import { i18n } from "../../../lib/langPack"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import toggleDisability from "../../../helpers/dom/toggleDisability"; export default class AppEditContactTab extends SliderSuperTab { private nameInputField: InputField; diff --git a/src/components/sidebarRight/tabs/gifs.ts b/src/components/sidebarRight/tabs/gifs.ts index 4a99889e..f1a058dd 100644 --- a/src/components/sidebarRight/tabs/gifs.ts +++ b/src/components/sidebarRight/tabs/gifs.ts @@ -11,11 +11,11 @@ import appSidebarRight from ".."; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appInlineBotsManager, { AppInlineBotsManager } from "../../../lib/appManagers/appInlineBotsManager"; import GifsMasonry from "../../gifsMasonry"; -import { attachClickEvent } from "../../../helpers/dom"; import appImManager from "../../../lib/appManagers/appImManager"; import type { MyDocument } from "../../../lib/appManagers/appDocsManager"; import mediaSizes from "../../../helpers/mediaSizes"; import findUpClassName from "../../../helpers/dom/findUpClassName"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; const ANIMATIONGROUP = 'GIFS-SEARCH'; diff --git a/src/components/sidebarRight/tabs/groupPermissions.ts b/src/components/sidebarRight/tabs/groupPermissions.ts index 5faa8093..05356805 100644 --- a/src/components/sidebarRight/tabs/groupPermissions.ts +++ b/src/components/sidebarRight/tabs/groupPermissions.ts @@ -4,8 +4,9 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { attachClickEvent, replaceContent } from "../../../helpers/dom"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import findUpTag from "../../../helpers/dom/findUpTag"; +import replaceContent from "../../../helpers/dom/replaceContent"; import ListenerSetter from "../../../helpers/listenerSetter"; import ScrollableLoader from "../../../helpers/listLoader"; import { ChannelParticipant, Chat, ChatBannedRights, Update } from "../../../layer"; diff --git a/src/components/sidebarRight/tabs/sharedMedia.ts b/src/components/sidebarRight/tabs/sharedMedia.ts index e5abd562..9511f864 100644 --- a/src/components/sidebarRight/tabs/sharedMedia.ts +++ b/src/components/sidebarRight/tabs/sharedMedia.ts @@ -15,7 +15,6 @@ import AppSearchSuper, { SearchSuperType } from "../../appSearchSuper."; import AvatarElement, { openAvatarViewer } from "../../avatar"; import SidebarSlider, { SliderSuperTab } from "../../slider"; import CheckboxField from "../../checkboxField"; -import { attachClickEvent, replaceContent, cancelEvent } from "../../../helpers/dom"; import appSidebarRight from ".."; import { TransitionSlider } from "../../transition"; import appNotificationsManager from "../../../lib/appManagers/appNotificationsManager"; @@ -46,6 +45,9 @@ import { isTouchSupported } from "../../../helpers/touchSupport"; import { isFirefox } from "../../../helpers/userAgent"; import appDownloadManager from "../../../lib/appManagers/appDownloadManager"; import ButtonCorner from "../../buttonCorner"; +import { cancelEvent } from "../../../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import replaceContent from "../../../helpers/dom/replaceContent"; let setText = (text: string, row: Row) => { //fastRaf(() => { diff --git a/src/components/sidebarRight/tabs/stickers.ts b/src/components/sidebarRight/tabs/stickers.ts index 727f581f..0d55cd4f 100644 --- a/src/components/sidebarRight/tabs/stickers.ts +++ b/src/components/sidebarRight/tabs/stickers.ts @@ -7,7 +7,6 @@ import { SliderSuperTab } from "../../slider"; import InputSearch from "../../inputSearch"; import LazyLoadQueue from "../../lazyLoadQueue"; -import { attachClickEvent } from "../../../helpers/dom"; import appImManager from "../../../lib/appManagers/appImManager"; import appStickersManager from "../../../lib/appManagers/appStickersManager"; import PopupStickers from "../../popups/stickers"; @@ -19,6 +18,7 @@ import { StickerSet, StickerSetCovered } from "../../../layer"; import { forEachReverse } from "../../../helpers/array"; import { i18n } from "../../../lib/langPack"; import findUpClassName from "../../../helpers/dom/findUpClassName"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; export default class AppStickersTab extends SliderSuperTab { private inputSearch: InputSearch; diff --git a/src/components/sidebarRight/tabs/userPermissions.ts b/src/components/sidebarRight/tabs/userPermissions.ts index fb9b38f9..ccd86675 100644 --- a/src/components/sidebarRight/tabs/userPermissions.ts +++ b/src/components/sidebarRight/tabs/userPermissions.ts @@ -4,16 +4,14 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { attachClickEvent, toggleDisability } from "../../../helpers/dom"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import toggleDisability from "../../../helpers/dom/toggleDisability"; import { deepEqual } from "../../../helpers/object"; import { ChannelParticipant } from "../../../layer"; import appChatsManager from "../../../lib/appManagers/appChatsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; -import appPeersManager from "../../../lib/appManagers/appPeersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import Button from "../../button"; -import { addCancelButton } from "../../popups"; -import PopupPeer from "../../popups/peer"; import { SettingSection } from "../../sidebarLeft"; import { SliderSuperTabEventable } from "../../sliderTab"; import { ChatPermissions } from "./groupPermissions"; diff --git a/src/components/slider.ts b/src/components/slider.ts index 087afd4b..cd9da583 100644 --- a/src/components/slider.ts +++ b/src/components/slider.ts @@ -4,12 +4,12 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { attachClickEvent } from "../helpers/dom"; import { horizontalMenu } from "./horizontalMenu"; import { TransitionSlider } from "./transition"; import appNavigationController, { NavigationItem } from "./appNavigationController"; import SliderSuperTab, { SliderSuperTabConstructable, SliderTab } from "./sliderTab"; import { safeAssign } from "../helpers/object"; +import { attachClickEvent } from "../helpers/dom/clickEvent"; const TRANSITION_TIME = 250; diff --git a/src/components/sortedUserList.ts b/src/components/sortedUserList.ts index bd67dbe9..5e4948ba 100644 --- a/src/components/sortedUserList.ts +++ b/src/components/sortedUserList.ts @@ -6,10 +6,12 @@ import type { LazyLoadQueueIntersector } from "./lazyLoadQueue"; import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager"; -import { isInDOM, positionElementByIndex, replaceContent } from "../helpers/dom"; import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck"; import appUsersManager from "../lib/appManagers/appUsersManager"; import { insertInDescendSortedArray } from "../helpers/array"; +import isInDOM from "../helpers/dom/isInDOM"; +import positionElementByIndex from "../helpers/dom/positionElementByIndex"; +import replaceContent from "../helpers/dom/replaceContent"; type SortedUser = { peerId: number, diff --git a/src/components/swipeHandler.ts b/src/components/swipeHandler.ts index 2b272d45..03f4a6e5 100644 --- a/src/components/swipeHandler.ts +++ b/src/components/swipeHandler.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { cancelEvent } from "../helpers/dom"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; import { safeAssign } from "../helpers/object"; import { isTouchSupported } from "../helpers/touchSupport"; diff --git a/src/components/transition.ts b/src/components/transition.ts index de6081ef..e7b72327 100644 --- a/src/components/transition.ts +++ b/src/components/transition.ts @@ -4,10 +4,10 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { whichChild } from "../helpers/dom"; import rootScope from "../lib/rootScope"; import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise"; import { dispatchHeavyAnimationEvent } from "../hooks/useHeavyAnimationCheck"; +import whichChild from "../helpers/dom/whichChild"; function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) { const width = prevTabContent.getBoundingClientRect().width; diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 32f8473d..b13a390a 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -17,7 +17,6 @@ import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; import appMessagesManager from '../lib/appManagers/appMessagesManager'; import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager'; import LottieLoader from '../lib/lottieLoader'; -import { attachClickEvent, cancelEvent, isInDOM } from "../helpers/dom"; import webpWorkerController from '../lib/webp/webpWorkerController'; import animationIntersector from './animationIntersector'; import appMediaPlaybackController from './appMediaPlaybackController'; @@ -40,6 +39,9 @@ import sequentialDom from '../helpers/sequentialDom'; import { fastRaf } from '../helpers/schedulers'; import appDownloadManager from '../lib/appManagers/appDownloadManager'; import appStickersManager from '../lib/appManagers/appStickersManager'; +import { cancelEvent } from '../helpers/dom/cancelEvent'; +import { attachClickEvent } from '../helpers/dom/clickEvent'; +import isInDOM from '../helpers/dom/isInDOM'; const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB diff --git a/src/helpers/calcImageInBox.ts b/src/helpers/calcImageInBox.ts new file mode 100644 index 00000000..99c5d79b --- /dev/null +++ b/src/helpers/calcImageInBox.ts @@ -0,0 +1,45 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + * + * Originally from: + * https://github.com/zhukov/webogram + * Copyright (C) 2014 Igor Zhukov + * https://github.com/zhukov/webogram/blob/master/LICENSE + */ + +import { MOUNT_CLASS_TO } from "../config/debug"; + +export default function calcImageInBox(imageW: number, imageH: number, boxW: number, boxH: number, noZoom = true) { + if(imageW < boxW && imageH < boxH && noZoom) { + return {w: imageW, h: imageH}; + } + + let boxedImageW = boxW; + let boxedImageH = boxH; + + if((imageW / imageH) > (boxW / boxH)) { + boxedImageH = (imageH * boxW / imageW) | 0; + } else { + boxedImageW = (imageW * boxH / imageH) | 0; + if(boxedImageW > boxW) { + boxedImageH = (boxedImageH * boxW / boxedImageW) | 0; + boxedImageW = boxW; + } + } + + // if (Config.Navigator.retina) { + // imageW = Math.floor(imageW / 2) + // imageH = Math.floor(imageH / 2) + // } + + if(noZoom && boxedImageW >= imageW && boxedImageH >= imageH) { + boxedImageW = imageW; + boxedImageH = imageH; + } + + return {w: boxedImageW, h: boxedImageH}; +} + +MOUNT_CLASS_TO.calcImageInBox = calcImageInBox; diff --git a/src/helpers/dom.ts b/src/helpers/dom.ts index e12a2dca..2ca61344 100644 --- a/src/helpers/dom.ts +++ b/src/helpers/dom.ts @@ -9,30 +9,6 @@ * https://github.com/zhukov/webogram/blob/master/LICENSE */ -import { MessageEntity } from "../layer"; -import RichTextProcessor from "../lib/richtextprocessor"; -import ListenerSetter from "./listenerSetter"; -import { isTouchSupported } from "./touchSupport"; -import { isApple, isMobile, isMobileSafari } from "./userAgent"; -import rootScope from "../lib/rootScope"; -import { MOUNT_CLASS_TO } from "../config/debug"; -import { doubleRaf } from "./schedulers"; - -/* export function isInDOM(element: Element, parentNode?: HTMLElement): boolean { - if(!element) { - return false; - } - - parentNode = parentNode || document.body; - if(element === parentNode) { - return true; - } - return isInDOM(element.parentNode as HTMLElement, parentNode); -} */ -export function isInDOM(element: Element): boolean { - return element?.isConnected; -} - /* export function checkDragEvent(e: any) { if(!e || e.target && (e.target.tagName === 'IMG' || e.target.tagName === 'A')) return false if(e.dataTransfer && e.dataTransfer.types) { @@ -48,46 +24,6 @@ export function isInDOM(element: Element): boolean { return false; } */ -export function cancelEvent(event: Event) { - event = event || window.event; - if(event) { - // @ts-ignore - event = event.originalEvent || event; - - try { - if(event.stopPropagation) event.stopPropagation(); - if(event.preventDefault) event.preventDefault(); - event.returnValue = false; - event.cancelBubble = true; - } catch(err) {} - } - - return false; -} - -export function placeCaretAtEnd(el: HTMLElement) { - if(isTouchSupported) { - return; - } - - el.focus(); - if(typeof window.getSelection !== "undefined" && typeof document.createRange !== "undefined") { - var range = document.createRange(); - range.selectNodeContents(el); - range.collapse(false); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - // @ts-ignore - } else if(typeof document.body.createTextRange !== "undefined") { - // @ts-ignore - var textRange = document.body.createTextRange(); - textRange.moveToElementText(el); - textRange.collapse(false); - textRange.select(); - } -} - /* export function getFieldSelection(field: any) { if(field.selectionStart) { return field.selectionStart; @@ -120,154 +56,6 @@ export function placeCaretAtEnd(el: HTMLElement) { return len; } */ -export function getRichValue(field: HTMLElement, entities?: MessageEntity[]) { - if(!field) { - return ''; - } - - const lines: string[] = []; - const line: string[] = []; - - getRichElementValue(field, lines, line, undefined, undefined, entities); - if(line.length) { - lines.push(line.join('')); - } - - let value = lines.join('\n'); - value = value.replace(/\u00A0/g, ' '); - - if(entities) { - RichTextProcessor.combineSameEntities(entities); - } - - //console.log('getRichValue:', value, entities); - - return value; -} - -MOUNT_CLASS_TO.getRichValue = getRichValue; - -export type MarkdownType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'monospace' | 'link'; -export type MarkdownTag = { - match: string, - entityName: 'messageEntityBold' | 'messageEntityUnderline' | 'messageEntityItalic' | 'messageEntityPre' | 'messageEntityStrike' | 'messageEntityTextUrl'; -}; -export const markdownTags: {[type in MarkdownType]: MarkdownTag} = { - bold: { - match: '[style*="font-weight"], b', - entityName: 'messageEntityBold' - }, - underline: { - match: '[style*="underline"], u', - entityName: 'messageEntityUnderline' - }, - italic: { - match: '[style*="italic"], i', - entityName: 'messageEntityItalic' - }, - monospace: { - match: '[style*="monospace"], [face="monospace"]', - entityName: 'messageEntityPre' - }, - strikethrough: { - match: '[style*="line-through"], strike', - entityName: 'messageEntityStrike' - }, - link: { - match: 'A', - entityName: 'messageEntityTextUrl' - } -}; -export function getRichElementValue(node: HTMLElement, lines: string[], line: string[], selNode?: Node, selOffset?: number, entities?: MessageEntity[], offset = {offset: 0}) { - if(node.nodeType === 3) { // TEXT - if(selNode === node) { - const value = node.nodeValue; - line.push(value.substr(0, selOffset) + '\x01' + value.substr(selOffset)); - } else { - const nodeValue = node.nodeValue; - line.push(nodeValue); - - if(entities && nodeValue.trim()) { - if(node.parentNode) { - const parentElement = node.parentElement; - - for(const type in markdownTags) { - const tag = markdownTags[type as MarkdownType]; - const closest = parentElement.closest(tag.match + ', [contenteditable]'); - if(closest && closest.getAttribute('contenteditable') === null) { - if(tag.entityName === 'messageEntityTextUrl') { - entities.push({ - _: tag.entityName as any, - url: (parentElement as HTMLAnchorElement).href, - offset: offset.offset, - length: nodeValue.length - }); - } else { - entities.push({ - _: tag.entityName as any, - offset: offset.offset, - length: nodeValue.length - }); - } - } - } - } - } - - offset.offset += nodeValue.length; - } - - return; - } - - if(node.nodeType !== 1) { // NON-ELEMENT - return; - } - - const isSelected = (selNode === node); - const isBlock = node.tagName === 'DIV' || node.tagName === 'P'; - if(isBlock && line.length || node.tagName === 'BR') { - lines.push(line.join('')); - line.splice(0, line.length); - } else if(node.tagName === 'IMG') { - const alt = (node as HTMLImageElement).alt; - if(alt) { - line.push(alt); - offset.offset += alt.length; - } - } - - if(isSelected && !selOffset) { - line.push('\x01'); - } - - let curChild = node.firstChild as HTMLElement; - while(curChild) { - getRichElementValue(curChild, lines, line, selNode, selOffset, entities, offset); - curChild = curChild.nextSibling as any; - } - - if(isSelected && selOffset) { - line.push('\x01'); - } - - if(isBlock && line.length) { - lines.push(line.join('')); - line.splice(0, line.length); - } -} - -export function isInputEmpty(element: HTMLElement) { - if(element.hasAttribute('contenteditable') || element.tagName !== 'INPUT') { - /* const value = element.innerText; - - return !value.trim() && !serializeNodes(Array.from(element.childNodes)).trim(); */ - return !getRichValue(element).trim(); - } else { - return !(element as HTMLInputElement).value.trim(); - } -} - /* export function serializeNodes(nodes: Node[]): string { return nodes.reduce((str, child: any) => { //console.log('childNode', str, child, typeof(child), typeof(child) === 'string', child.innerText); @@ -293,242 +81,6 @@ export function isInputEmpty(element: HTMLElement) { } } */ -// generate a path's arc data parameter -// http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands -var arcParameter = function(rx: number, ry: number, xAxisRotation: number, largeArcFlag: number, sweepFlag: number, x: number, y: number) { - return [rx, ',', ry, ' ', - xAxisRotation, ' ', - largeArcFlag, ',', - sweepFlag, ' ', - x, ',', y ].join(''); -}; - -export function generatePathData(x: number, y: number, width: number, height: number, tl: number, tr: number, br: number, bl: number) { - const data: string[] = []; - - // start point in top-middle of the rectangle - data.push('M' + (x + width / 2) + ',' + y); - - // next we go to the right - data.push('H' + (x + width - tr)); - - if(tr > 0) { - // now we draw the arc in the top-right corner - data.push('A' + arcParameter(tr, tr, 0, 0, 1, (x + width), (y + tr))); - } - - // next we go down - data.push('V' + (y + height - br)); - - if(br > 0) { - // now we draw the arc in the lower-right corner - data.push('A' + arcParameter(br, br, 0, 0, 1, (x + width - br), (y + height))); - } - - // now we go to the left - data.push('H' + (x + bl)); - - if(bl > 0) { - // now we draw the arc in the lower-left corner - data.push('A' + arcParameter(bl, bl, 0, 0, 1, (x + 0), (y + height - bl))); - } - - // next we go up - data.push('V' + (y + tl)); - - if(tl > 0) { - // now we draw the arc in the top-left corner - data.push('A' + arcParameter(tl, tl, 0, 0, 1, (x + tl), (y + 0))); - } - - // and we close the path - data.push('Z'); - - return data.join(' '); -}; - -MOUNT_CLASS_TO.generatePathData = generatePathData; - -export function whichChild(elem: Node) { - if(!elem.parentNode) { - return -1; - } - - let i = 0; - // @ts-ignore - while((elem = elem.previousElementSibling) !== null) ++i; - return i; -}; - -export function fillPropertyValue(str: string) { - let splitted = str.split(' '); - if(splitted.length !== 4) { - if(!splitted[0]) splitted[0] = '0px'; - for(let i = splitted.length; i < 4; ++i) { - splitted[i] = splitted[i % 2] || splitted[0] || '0px'; - } - } - - return splitted; -} - -export function calcImageInBox(imageW: number, imageH: number, boxW: number, boxH: number, noZoom = true) { - if(imageW < boxW && imageH < boxH && noZoom) { - return {w: imageW, h: imageH}; - } - - let boxedImageW = boxW; - let boxedImageH = boxH; - - if((imageW / imageH) > (boxW / boxH)) { - boxedImageH = (imageH * boxW / imageW) | 0; - } else { - boxedImageW = (imageW * boxH / imageH) | 0; - if(boxedImageW > boxW) { - boxedImageH = (boxedImageH * boxW / boxedImageW) | 0; - boxedImageW = boxW; - } - } - - // if (Config.Navigator.retina) { - // imageW = Math.floor(imageW / 2) - // imageH = Math.floor(imageH / 2) - // } - - if(noZoom && boxedImageW >= imageW && boxedImageH >= imageH) { - boxedImageW = imageW; - boxedImageH = imageH; - } - - return {w: boxedImageW, h: boxedImageH}; -} - -MOUNT_CLASS_TO.calcImageInBox = calcImageInBox; - -export function positionElementByIndex(element: HTMLElement, container: HTMLElement, pos: number, prevPos?: number) { - if(prevPos === undefined) { - prevPos = element.parentElement === container ? whichChild(element) : -1; - } - - if(prevPos === pos) { - return false; - } else if(prevPos !== -1 && prevPos < pos) { // was higher - pos += 1; - } - - if(container.childElementCount > pos) { - container.insertBefore(element, container.children[pos]); - } else { - container.append(element); - } - - return true; -} - -export function cancelSelection() { - if(window.getSelection) { - if(window.getSelection().empty) { // Chrome - window.getSelection().empty(); - } else if(window.getSelection().removeAllRanges) { // Firefox - window.getSelection().removeAllRanges(); - } - // @ts-ignore - } else if(document.selection) { // IE? - // @ts-ignore - document.selection.empty(); - } -} - -//(window as any).splitStringByLength = splitStringByLength; - -export function getSelectedText(): string { - if(window.getSelection) { - return window.getSelection().toString(); - // @ts-ignore - } else if(document.selection) { - // @ts-ignore - return document.selection.createRange().text; - } - - return ''; -} - -export function blurActiveElement() { - if(document.activeElement && (document.activeElement as HTMLInputElement).blur) { - (document.activeElement as HTMLInputElement).blur(); - return true; - } - - return false; -} - -export const CLICK_EVENT_NAME: 'mousedown' | 'touchend' | 'click' = (isTouchSupported ? 'mousedown' : 'click') as any; -export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>; -export const attachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options: AttachClickOptions = {}) => { - const add = options.listenerSetter ? options.listenerSetter.add.bind(options.listenerSetter, elem) : elem.addEventListener.bind(elem); - const remove = options.listenerSetter ? options.listenerSetter.removeManual.bind(options.listenerSetter, elem) : elem.removeEventListener.bind(elem); - - options.touchMouseDown = true; - /* if(options.touchMouseDown && CLICK_EVENT_NAME === 'touchend') { - add('mousedown', callback, options); - } else if(CLICK_EVENT_NAME === 'touchend') { - const o = {...options, once: true}; - - const onTouchStart = (e: TouchEvent) => { - const onTouchMove = (e: TouchEvent) => { - remove('touchmove', onTouchMove, o); - remove('touchend', onTouchEnd, o); - }; - - const onTouchEnd = (e: TouchEvent) => { - remove('touchmove', onTouchMove, o); - callback(e); - if(options.once) { - remove('touchstart', onTouchStart); - } - }; - - add('touchend', onTouchEnd, o); - add('touchmove', onTouchMove, o); - }; - - add('touchstart', onTouchStart); - } else { - add(CLICK_EVENT_NAME, callback, options); - } */ - add(CLICK_EVENT_NAME, callback, options); -}; - -export const detachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options?: AddEventListenerOptions) => { - if(CLICK_EVENT_NAME === 'touchend') { - elem.removeEventListener('touchstart', callback, options); - } else { - elem.removeEventListener(CLICK_EVENT_NAME, callback, options); - } -}; - -export const getSelectedNodes = () => { - const nodes: Node[] = []; - const selection = window.getSelection(); - for(let i = 0; i < selection.rangeCount; ++i) { - const range = selection.getRangeAt(i); - let {startContainer, endContainer} = range; - if(endContainer.nodeType !== 3) endContainer = endContainer.firstChild; - - while(startContainer && startContainer !== endContainer) { - nodes.push(startContainer.nodeType === 3 ? startContainer : startContainer.firstChild); - startContainer = startContainer.nextSibling; - } - - if(nodes[nodes.length - 1] !== endContainer) { - nodes.push(endContainer); - } - } - - // * filter null's due to
- return nodes.filter(node => !!node); -}; - /* export const isSelectionSingle = (input: Element = document.activeElement) => { const nodes = getSelectedNodes(); const parents = [...new Set(nodes.map(node => node.parentNode))]; @@ -547,46 +99,6 @@ export const getSelectedNodes = () => { return single; }; */ -export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', callback: () => void, listenerSetter: ListenerSetter) => { - if(isTouchSupported) { - let lastY: number; - const options = {passive: true}; - listenerSetter.add(elem, 'touchstart', (e) => { - if(e.touches.length > 1) { - onTouchEnd(); - return; - } - - lastY = e.touches[0].clientY; - - listenerSetter.add(elem, 'touchmove', onTouchMove, options); - listenerSetter.add(elem, 'touchend', onTouchEnd, options); - }, options); - - const onTouchMove = (e: TouchEvent) => { - const clientY = e.touches[0].clientY; - - const isDown = clientY < lastY; - if(side === 'bottom' && isDown) callback(); - else if(side === 'top' && !isDown) callback(); - lastY = clientY; - //alert('isDown: ' + !!isDown); - }; - - const onTouchEnd = () => { - listenerSetter.removeManual(elem, 'touchmove', onTouchMove, options); - listenerSetter.removeManual(elem, 'touchend', onTouchEnd, options); - }; - } else { - listenerSetter.add(elem, 'wheel', (e) => { - const isDown = e.deltaY > 0; - //this.log('wheel', e, isDown); - if(side === 'bottom' && isDown) callback(); - else if(side === 'top' && !isDown) callback(); - }, {passive: true}); - } -}; - /* export function radiosHandleChange(inputs: HTMLInputElement[], onChange: (value: string) => void) { inputs.forEach(input => { input.addEventListener('change', () => { @@ -597,105 +109,4 @@ export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', }); } */ -export function isSendShortcutPressed(e: KeyboardEvent) { - if(e.key === 'Enter' && !isMobile && !e.isComposing) { - /* if(e.ctrlKey || e.metaKey) { - this.messageInput.innerHTML += '
'; - placeCaretAtEnd(this.message) - return; - } */ - - if(rootScope.settings.sendShortcut === 'enter') { - if(e.shiftKey || e.ctrlKey || e.metaKey) { - return; - } - - return true; - } else { - const secondaryKey = isApple ? e.metaKey : e.ctrlKey; - if(e.shiftKey || (isApple ? e.ctrlKey : e.metaKey)) { - return; - } - - if(secondaryKey) { - return true; - } - } - } - - return false; -} - -export function reflowScrollableElement(element: HTMLElement) { - element.style.display = 'none'; - void element.offsetLeft; // reflow - element.style.display = ''; -} - -export function isSelectionEmpty(selection = window.getSelection()) { - if(!selection || !selection.rangeCount) { - return true; - } - - const selectionRange = selection.getRangeAt(0); - if(!selectionRange.toString() || !selectionRange.START_TO_END) { - return true; - } - - return false; -} - -export function disableTransition(elements: HTMLElement[]) { - elements.forEach(el => el.classList.add('no-transition')); - - doubleRaf().then(() => { - elements.forEach(el => el.classList.remove('no-transition')); - }); -} - -export function toggleDisability(elements: HTMLElement[], disable: boolean) { - if(disable) { - elements.forEach(el => el.setAttribute('disabled', 'true')); - } else { - elements.forEach(el => el.removeAttribute('disabled')); - } - - return () => toggleDisability(elements, !disable); -} - -export function canFocus(isFirstInput: boolean) { - return !isMobileSafari || !isFirstInput; -} - -export function htmlToDocumentFragment(html: string) { - var template = document.createElement('template'); - html = html.trim(); // Never return a text node of whitespace as the result - template.innerHTML = html; - return template.content; -} - -export function htmlToSpan(html: string) { - const span = document.createElement('span'); - span.innerHTML = html; - return span; -} - -export function replaceContent(elem: HTMLElement, node: string | Node) { - // * children.length doesn't count text nodes - const firstChild = elem.firstChild; - if(firstChild) { - if(elem.lastChild === firstChild) { - firstChild.replaceWith(node); - } else { - elem.textContent = ''; - elem.append(node); - } - } else { - elem.append(node); - } -} - -export function setInnerHTML(elem: HTMLElement, html: string) { - elem.setAttribute('dir', 'auto'); - elem.innerHTML = html; -} +export default {}; diff --git a/src/helpers/dom/blurActiveElement.ts b/src/helpers/dom/blurActiveElement.ts new file mode 100644 index 00000000..83f02abc --- /dev/null +++ b/src/helpers/dom/blurActiveElement.ts @@ -0,0 +1,14 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function blurActiveElement() { + if(document.activeElement && (document.activeElement as HTMLInputElement).blur) { + (document.activeElement as HTMLInputElement).blur(); + return true; + } + + return false; +} diff --git a/src/helpers/dom/canFocus.ts b/src/helpers/dom/canFocus.ts new file mode 100644 index 00000000..97dc6231 --- /dev/null +++ b/src/helpers/dom/canFocus.ts @@ -0,0 +1,11 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import { isMobileSafari } from "../userAgent"; + +export function canFocus(isFirstInput: boolean) { + return !isMobileSafari || !isFirstInput; +} diff --git a/src/helpers/dom/cancelEvent.ts b/src/helpers/dom/cancelEvent.ts new file mode 100644 index 00000000..6048c908 --- /dev/null +++ b/src/helpers/dom/cancelEvent.ts @@ -0,0 +1,27 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + * + * Originally from: + * https://github.com/zhukov/webogram + * Copyright (C) 2014 Igor Zhukov + * https://github.com/zhukov/webogram/blob/master/LICENSE + */ + +export function cancelEvent(event: Event) { + event = event || window.event; + if(event) { + // @ts-ignore + event = event.originalEvent || event; + + try { + if(event.stopPropagation) event.stopPropagation(); + if(event.preventDefault) event.preventDefault(); + event.returnValue = false; + event.cancelBubble = true; + } catch(err) {} + } + + return false; +} diff --git a/src/helpers/dom/cancelSelection.ts b/src/helpers/dom/cancelSelection.ts new file mode 100644 index 00000000..b2d4b7a2 --- /dev/null +++ b/src/helpers/dom/cancelSelection.ts @@ -0,0 +1,19 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function cancelSelection() { + if(window.getSelection) { + if(window.getSelection().empty) { // Chrome + window.getSelection().empty(); + } else if(window.getSelection().removeAllRanges) { // Firefox + window.getSelection().removeAllRanges(); + } + // @ts-ignore + } else if(document.selection) { // IE? + // @ts-ignore + document.selection.empty(); + } +} diff --git a/src/helpers/dom/clickEvent.ts b/src/helpers/dom/clickEvent.ts new file mode 100644 index 00000000..5ff88065 --- /dev/null +++ b/src/helpers/dom/clickEvent.ts @@ -0,0 +1,53 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import type ListenerSetter from "../listenerSetter"; +import { isTouchSupported } from "../touchSupport"; + +export const CLICK_EVENT_NAME: 'mousedown' | 'touchend' | 'click' = (isTouchSupported ? 'mousedown' : 'click') as any; +export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>; +export function attachClickEvent(elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options: AttachClickOptions = {}) { + const add = options.listenerSetter ? options.listenerSetter.add.bind(options.listenerSetter, elem) : elem.addEventListener.bind(elem); + const remove = options.listenerSetter ? options.listenerSetter.removeManual.bind(options.listenerSetter, elem) : elem.removeEventListener.bind(elem); + + options.touchMouseDown = true; + /* if(options.touchMouseDown && CLICK_EVENT_NAME === 'touchend') { + add('mousedown', callback, options); + } else if(CLICK_EVENT_NAME === 'touchend') { + const o = {...options, once: true}; + + const onTouchStart = (e: TouchEvent) => { + const onTouchMove = (e: TouchEvent) => { + remove('touchmove', onTouchMove, o); + remove('touchend', onTouchEnd, o); + }; + + const onTouchEnd = (e: TouchEvent) => { + remove('touchmove', onTouchMove, o); + callback(e); + if(options.once) { + remove('touchstart', onTouchStart); + } + }; + + add('touchend', onTouchEnd, o); + add('touchmove', onTouchMove, o); + }; + + add('touchstart', onTouchStart); + } else { + add(CLICK_EVENT_NAME, callback, options); + } */ + add(CLICK_EVENT_NAME, callback, options); +} + +export function detachClickEvent(elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options?: AddEventListenerOptions) { + if(CLICK_EVENT_NAME === 'touchend') { + elem.removeEventListener('touchstart', callback, options); + } else { + elem.removeEventListener(CLICK_EVENT_NAME, callback, options); + } +} diff --git a/src/helpers/dom/disableTransition.ts b/src/helpers/dom/disableTransition.ts new file mode 100644 index 00000000..d316fd10 --- /dev/null +++ b/src/helpers/dom/disableTransition.ts @@ -0,0 +1,15 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import { doubleRaf } from "../schedulers"; + +export default function disableTransition(elements: HTMLElement[]) { + elements.forEach(el => el.classList.add('no-transition')); + + doubleRaf().then(() => { + elements.forEach(el => el.classList.remove('no-transition')); + }); +} diff --git a/src/helpers/dom/getRichValue.ts b/src/helpers/dom/getRichValue.ts new file mode 100644 index 00000000..1f91b36b --- /dev/null +++ b/src/helpers/dom/getRichValue.ts @@ -0,0 +1,152 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + * + * Originally from: + * https://github.com/zhukov/webogram + * Copyright (C) 2014 Igor Zhukov + * https://github.com/zhukov/webogram/blob/master/LICENSE + */ + +import { MOUNT_CLASS_TO } from "../../config/debug"; +import { MessageEntity } from "../../layer"; +import RichTextProcessor from "../../lib/richtextprocessor"; + +export default function getRichValue(field: HTMLElement, entities?: MessageEntity[]) { + if(!field) { + return ''; + } + + const lines: string[] = []; + const line: string[] = []; + + getRichElementValue(field, lines, line, undefined, undefined, entities); + if(line.length) { + lines.push(line.join('')); + } + + let value = lines.join('\n'); + value = value.replace(/\u00A0/g, ' '); + + if(entities) { + RichTextProcessor.combineSameEntities(entities); + } + + //console.log('getRichValue:', value, entities); + + return value; +} + +MOUNT_CLASS_TO.getRichValue = getRichValue; + +export type MarkdownType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'monospace' | 'link'; +export type MarkdownTag = { + match: string, + entityName: 'messageEntityBold' | 'messageEntityUnderline' | 'messageEntityItalic' | 'messageEntityPre' | 'messageEntityStrike' | 'messageEntityTextUrl'; +}; +export const markdownTags: {[type in MarkdownType]: MarkdownTag} = { + bold: { + match: '[style*="font-weight"], b', + entityName: 'messageEntityBold' + }, + underline: { + match: '[style*="underline"], u', + entityName: 'messageEntityUnderline' + }, + italic: { + match: '[style*="italic"], i', + entityName: 'messageEntityItalic' + }, + monospace: { + match: '[style*="monospace"], [face="monospace"]', + entityName: 'messageEntityPre' + }, + strikethrough: { + match: '[style*="line-through"], strike', + entityName: 'messageEntityStrike' + }, + link: { + match: 'A', + entityName: 'messageEntityTextUrl' + } +}; + +function getRichElementValue(node: HTMLElement, lines: string[], line: string[], selNode?: Node, selOffset?: number, entities?: MessageEntity[], offset = {offset: 0}) { + if(node.nodeType === 3) { // TEXT + if(selNode === node) { + const value = node.nodeValue; + line.push(value.substr(0, selOffset) + '\x01' + value.substr(selOffset)); + } else { + const nodeValue = node.nodeValue; + line.push(nodeValue); + + if(entities && nodeValue.trim()) { + if(node.parentNode) { + const parentElement = node.parentElement; + + for(const type in markdownTags) { + const tag = markdownTags[type as MarkdownType]; + const closest = parentElement.closest(tag.match + ', [contenteditable]'); + if(closest && closest.getAttribute('contenteditable') === null) { + if(tag.entityName === 'messageEntityTextUrl') { + entities.push({ + _: tag.entityName as any, + url: (parentElement as HTMLAnchorElement).href, + offset: offset.offset, + length: nodeValue.length + }); + } else { + entities.push({ + _: tag.entityName as any, + offset: offset.offset, + length: nodeValue.length + }); + } + } + } + } + } + + offset.offset += nodeValue.length; + } + + return; + } + + if(node.nodeType !== 1) { // NON-ELEMENT + return; + } + + const isSelected = (selNode === node); + const isBlock = node.tagName === 'DIV' || node.tagName === 'P'; + if(isBlock && line.length || node.tagName === 'BR') { + lines.push(line.join('')); + line.splice(0, line.length); + } else if(node.tagName === 'IMG') { + const alt = (node as HTMLImageElement).alt; + if(alt) { + line.push(alt); + offset.offset += alt.length; + } + } + + if(isSelected && !selOffset) { + line.push('\x01'); + } + + let curChild = node.firstChild as HTMLElement; + while(curChild) { + getRichElementValue(curChild, lines, line, selNode, selOffset, entities, offset); + curChild = curChild.nextSibling as any; + } + + if(isSelected && selOffset) { + line.push('\x01'); + } + + if(isBlock && line.length) { + lines.push(line.join('')); + line.splice(0, line.length); + } +} diff --git a/src/helpers/dom/getSelectedNodes.ts b/src/helpers/dom/getSelectedNodes.ts new file mode 100644 index 00000000..9dd8d847 --- /dev/null +++ b/src/helpers/dom/getSelectedNodes.ts @@ -0,0 +1,27 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function getSelectedNodes() { + const nodes: Node[] = []; + const selection = window.getSelection(); + for(let i = 0; i < selection.rangeCount; ++i) { + const range = selection.getRangeAt(i); + let {startContainer, endContainer} = range; + if(endContainer.nodeType !== 3) endContainer = endContainer.firstChild; + + while(startContainer && startContainer !== endContainer) { + nodes.push(startContainer.nodeType === 3 ? startContainer : startContainer.firstChild); + startContainer = startContainer.nextSibling; + } + + if(nodes[nodes.length - 1] !== endContainer) { + nodes.push(endContainer); + } + } + + // * filter null's due to
+ return nodes.filter(node => !!node); +} diff --git a/src/helpers/dom/getSelectedText.ts b/src/helpers/dom/getSelectedText.ts new file mode 100644 index 00000000..4a269dae --- /dev/null +++ b/src/helpers/dom/getSelectedText.ts @@ -0,0 +1,17 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function getSelectedText(): string { + if(window.getSelection) { + return window.getSelection().toString(); + // @ts-ignore + } else if(document.selection) { + // @ts-ignore + return document.selection.createRange().text; + } + + return ''; +} diff --git a/src/helpers/dom/handleScrollSideEvent.ts b/src/helpers/dom/handleScrollSideEvent.ts new file mode 100644 index 00000000..af383c73 --- /dev/null +++ b/src/helpers/dom/handleScrollSideEvent.ts @@ -0,0 +1,48 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import type ListenerSetter from "../listenerSetter"; +import { isTouchSupported } from "../touchSupport"; + +export default function handleScrollSideEvent(elem: HTMLElement, side: 'top' | 'bottom', callback: () => void, listenerSetter: ListenerSetter) { + if(isTouchSupported) { + let lastY: number; + const options = {passive: true}; + listenerSetter.add(elem, 'touchstart', (e) => { + if(e.touches.length > 1) { + onTouchEnd(); + return; + } + + lastY = e.touches[0].clientY; + + listenerSetter.add(elem, 'touchmove', onTouchMove, options); + listenerSetter.add(elem, 'touchend', onTouchEnd, options); + }, options); + + const onTouchMove = (e: TouchEvent) => { + const clientY = e.touches[0].clientY; + + const isDown = clientY < lastY; + if(side === 'bottom' && isDown) callback(); + else if(side === 'top' && !isDown) callback(); + lastY = clientY; + //alert('isDown: ' + !!isDown); + }; + + const onTouchEnd = () => { + listenerSetter.removeManual(elem, 'touchmove', onTouchMove, options); + listenerSetter.removeManual(elem, 'touchend', onTouchEnd, options); + }; + } else { + listenerSetter.add(elem, 'wheel', (e) => { + const isDown = e.deltaY > 0; + //this.log('wheel', e, isDown); + if(side === 'bottom' && isDown) callback(); + else if(side === 'top' && !isDown) callback(); + }, {passive: true}); + } +} diff --git a/src/helpers/dom/htmlToDocumentFragment.ts b/src/helpers/dom/htmlToDocumentFragment.ts new file mode 100644 index 00000000..53bd0dc7 --- /dev/null +++ b/src/helpers/dom/htmlToDocumentFragment.ts @@ -0,0 +1,12 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function htmlToDocumentFragment(html: string) { + var template = document.createElement('template'); + html = html.trim(); // Never return a text node of whitespace as the result + template.innerHTML = html; + return template.content; +} diff --git a/src/helpers/dom/htmlToSpan.ts b/src/helpers/dom/htmlToSpan.ts new file mode 100644 index 00000000..b7d4251a --- /dev/null +++ b/src/helpers/dom/htmlToSpan.ts @@ -0,0 +1,11 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function htmlToSpan(html: string) { + const span = document.createElement('span'); + span.innerHTML = html; + return span; +} diff --git a/src/helpers/dom/isInDOM.ts b/src/helpers/dom/isInDOM.ts new file mode 100644 index 00000000..2a1c5611 --- /dev/null +++ b/src/helpers/dom/isInDOM.ts @@ -0,0 +1,25 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + * + * Originally from: + * https://github.com/zhukov/webogram + * Copyright (C) 2014 Igor Zhukov + * https://github.com/zhukov/webogram/blob/master/LICENSE + */ + +/* export function isInDOM(element: Element, parentNode?: HTMLElement): boolean { + if(!element) { + return false; + } + + parentNode = parentNode || document.body; + if(element === parentNode) { + return true; + } + return isInDOM(element.parentNode as HTMLElement, parentNode); +} */ +export default function isInDOM(element: Element): boolean { + return element?.isConnected; +} diff --git a/src/helpers/dom/isInputEmpty.ts b/src/helpers/dom/isInputEmpty.ts new file mode 100644 index 00000000..0e16732b --- /dev/null +++ b/src/helpers/dom/isInputEmpty.ts @@ -0,0 +1,18 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import getRichValue from "./getRichValue"; + +export default function isInputEmpty(element: HTMLElement) { + if(element.hasAttribute('contenteditable') || element.tagName !== 'INPUT') { + /* const value = element.innerText; + + return !value.trim() && !serializeNodes(Array.from(element.childNodes)).trim(); */ + return !getRichValue(element).trim(); + } else { + return !(element as HTMLInputElement).value.trim(); + } +} diff --git a/src/helpers/dom/isSelectionEmpty.ts b/src/helpers/dom/isSelectionEmpty.ts new file mode 100644 index 00000000..96156698 --- /dev/null +++ b/src/helpers/dom/isSelectionEmpty.ts @@ -0,0 +1,18 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function isSelectionEmpty(selection = window.getSelection()) { + if(!selection || !selection.rangeCount) { + return true; + } + + const selectionRange = selection.getRangeAt(0); + if(!selectionRange.toString() || !selectionRange.START_TO_END) { + return true; + } + + return false; +} diff --git a/src/helpers/dom/isSendShortcutPressed.ts b/src/helpers/dom/isSendShortcutPressed.ts new file mode 100644 index 00000000..1d9461b8 --- /dev/null +++ b/src/helpers/dom/isSendShortcutPressed.ts @@ -0,0 +1,37 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import rootScope from "../../lib/rootScope"; +import { isMobile, isApple } from "../userAgent"; + +export default function isSendShortcutPressed(e: KeyboardEvent) { + if(e.key === 'Enter' && !isMobile && !e.isComposing) { + /* if(e.ctrlKey || e.metaKey) { + this.messageInput.innerHTML += '
'; + placeCaretAtEnd(this.message) + return; + } */ + + if(rootScope.settings.sendShortcut === 'enter') { + if(e.shiftKey || e.ctrlKey || e.metaKey) { + return; + } + + return true; + } else { + const secondaryKey = isApple ? e.metaKey : e.ctrlKey; + if(e.shiftKey || (isApple ? e.ctrlKey : e.metaKey)) { + return; + } + + if(secondaryKey) { + return true; + } + } + } + + return false; +} diff --git a/src/helpers/dom/placeCaretAtEnd.ts b/src/helpers/dom/placeCaretAtEnd.ts new file mode 100644 index 00000000..571d3bac --- /dev/null +++ b/src/helpers/dom/placeCaretAtEnd.ts @@ -0,0 +1,35 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + * + * Originally from: + * https://github.com/zhukov/webogram + * Copyright (C) 2014 Igor Zhukov + * https://github.com/zhukov/webogram/blob/master/LICENSE + */ + +import { isTouchSupported } from "../touchSupport"; + +export default function placeCaretAtEnd(el: HTMLElement) { + if(isTouchSupported) { + return; + } + + el.focus(); + if(typeof window.getSelection !== "undefined" && typeof document.createRange !== "undefined") { + var range = document.createRange(); + range.selectNodeContents(el); + range.collapse(false); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + // @ts-ignore + } else if(typeof document.body.createTextRange !== "undefined") { + // @ts-ignore + var textRange = document.body.createTextRange(); + textRange.moveToElementText(el); + textRange.collapse(false); + textRange.select(); + } +} diff --git a/src/helpers/dom/positionElementByIndex.ts b/src/helpers/dom/positionElementByIndex.ts new file mode 100644 index 00000000..47f36215 --- /dev/null +++ b/src/helpers/dom/positionElementByIndex.ts @@ -0,0 +1,27 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import whichChild from "./whichChild"; + +export default function positionElementByIndex(element: HTMLElement, container: HTMLElement, pos: number, prevPos?: number) { + if(prevPos === undefined) { + prevPos = element.parentElement === container ? whichChild(element) : -1; + } + + if(prevPos === pos) { + return false; + } else if(prevPos !== -1 && prevPos < pos) { // was higher + pos += 1; + } + + if(container.childElementCount > pos) { + container.insertBefore(element, container.children[pos]); + } else { + container.append(element); + } + + return true; +} diff --git a/src/helpers/dom/reflowScrollableElement.ts b/src/helpers/dom/reflowScrollableElement.ts new file mode 100644 index 00000000..258de51a --- /dev/null +++ b/src/helpers/dom/reflowScrollableElement.ts @@ -0,0 +1,11 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function reflowScrollableElement(element: HTMLElement) { + element.style.display = 'none'; + void element.offsetLeft; // reflow + element.style.display = ''; +} diff --git a/src/helpers/dom/replaceContent.ts b/src/helpers/dom/replaceContent.ts new file mode 100644 index 00000000..c6cba8be --- /dev/null +++ b/src/helpers/dom/replaceContent.ts @@ -0,0 +1,20 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function replaceContent(elem: HTMLElement, node: string | Node) { + // * children.length doesn't count text nodes + const firstChild = elem.firstChild; + if(firstChild) { + if(elem.lastChild === firstChild) { + firstChild.replaceWith(node); + } else { + elem.textContent = ''; + elem.append(node); + } + } else { + elem.append(node); + } +} diff --git a/src/helpers/dom/setInnerHTML.ts b/src/helpers/dom/setInnerHTML.ts new file mode 100644 index 00000000..6b0a2d78 --- /dev/null +++ b/src/helpers/dom/setInnerHTML.ts @@ -0,0 +1,10 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function setInnerHTML(elem: HTMLElement, html: string) { + elem.setAttribute('dir', 'auto'); + elem.innerHTML = html; +} diff --git a/src/helpers/dom/toggleDisability.ts b/src/helpers/dom/toggleDisability.ts new file mode 100644 index 00000000..79189ee0 --- /dev/null +++ b/src/helpers/dom/toggleDisability.ts @@ -0,0 +1,15 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function toggleDisability(elements: HTMLElement[], disable: boolean) { + if(disable) { + elements.forEach(el => el.setAttribute('disabled', 'true')); + } else { + elements.forEach(el => el.removeAttribute('disabled')); + } + + return () => toggleDisability(elements, !disable); +} diff --git a/src/helpers/dom/whichChild.ts b/src/helpers/dom/whichChild.ts new file mode 100644 index 00000000..1bddb82a --- /dev/null +++ b/src/helpers/dom/whichChild.ts @@ -0,0 +1,16 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function whichChild(elem: Node) { + if(!elem.parentNode) { + return -1; + } + + let i = 0; + // @ts-ignore + while((elem = elem.previousElementSibling) !== null) ++i; + return i; +} diff --git a/src/helpers/fastSmoothScroll.ts b/src/helpers/fastSmoothScroll.ts index f9ed9652..44f298d7 100644 --- a/src/helpers/fastSmoothScroll.ts +++ b/src/helpers/fastSmoothScroll.ts @@ -10,7 +10,7 @@ import { dispatchHeavyAnimationEvent } from '../hooks/useHeavyAnimationCheck'; import { fastRaf } from './schedulers'; import { animateSingle, cancelAnimationByKey } from './animation'; import rootScope from '../lib/rootScope'; -import { isInDOM } from './dom'; +import isInDOM from './dom/isInDOM'; const MAX_DISTANCE = 1500; const MIN_JS_DURATION = 250; diff --git a/src/helpers/fillPropertyValue.ts b/src/helpers/fillPropertyValue.ts new file mode 100644 index 00000000..ccbaf8b6 --- /dev/null +++ b/src/helpers/fillPropertyValue.ts @@ -0,0 +1,17 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +export default function fillPropertyValue(str: string) { + let splitted = str.split(' '); + if(splitted.length !== 4) { + if(!splitted[0]) splitted[0] = '0px'; + for(let i = splitted.length; i < 4; ++i) { + splitted[i] = splitted[i % 2] || splitted[0] || '0px'; + } + } + + return splitted; +} diff --git a/src/helpers/generatePathData.ts b/src/helpers/generatePathData.ts new file mode 100644 index 00000000..2a71d2d1 --- /dev/null +++ b/src/helpers/generatePathData.ts @@ -0,0 +1,64 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +// generate a path's arc data parameter + +import { MOUNT_CLASS_TO } from "../config/debug"; + +// http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands +function arcParameter(rx: number, ry: number, xAxisRotation: number, largeArcFlag: number, sweepFlag: number, x: number, y: number) { + return [rx, ',', ry, ' ', + xAxisRotation, ' ', + largeArcFlag, ',', + sweepFlag, ' ', + x, ',', y ].join(''); +} + +export default function generatePathData(x: number, y: number, width: number, height: number, tl: number, tr: number, br: number, bl: number) { + const data: string[] = []; + + // start point in top-middle of the rectangle + data.push('M' + (x + width / 2) + ',' + y); + + // next we go to the right + data.push('H' + (x + width - tr)); + + if(tr > 0) { + // now we draw the arc in the top-right corner + data.push('A' + arcParameter(tr, tr, 0, 0, 1, (x + width), (y + tr))); + } + + // next we go down + data.push('V' + (y + height - br)); + + if(br > 0) { + // now we draw the arc in the lower-right corner + data.push('A' + arcParameter(br, br, 0, 0, 1, (x + width - br), (y + height))); + } + + // now we go to the left + data.push('H' + (x + bl)); + + if(bl > 0) { + // now we draw the arc in the lower-left corner + data.push('A' + arcParameter(bl, bl, 0, 0, 1, (x + 0), (y + height - bl))); + } + + // next we go up + data.push('V' + (y + tl)); + + if(tl > 0) { + // now we draw the arc in the top-left corner + data.push('A' + arcParameter(tl, tl, 0, 0, 1, (x + tl), (y + 0))); + } + + // and we close the path + data.push('Z'); + + return data.join(' '); +} + +MOUNT_CLASS_TO.generatePathData = generatePathData; diff --git a/src/helpers/sequentialDom.ts b/src/helpers/sequentialDom.ts index 47f06d7e..31f23379 100644 --- a/src/helpers/sequentialDom.ts +++ b/src/helpers/sequentialDom.ts @@ -1,7 +1,7 @@ import { fastRaf } from "./schedulers"; import { CancellablePromise, deferredPromise } from "./cancellablePromise"; -import { isInDOM } from "./dom"; import { MOUNT_CLASS_TO } from "../config/debug"; +import isInDOM from "./dom/isInDOM"; class SequentialDom { private promises: Partial<{ diff --git a/src/index.ts b/src/index.ts index 332255fa..16b3c318 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ */ import App from './config/app'; +import blurActiveElement from './helpers/dom/blurActiveElement'; import findUpClassName from './helpers/dom/findUpClassName'; import fixSafariStickyInput from './helpers/dom/fixSafariStickyInput'; import { isMobileSafari } from './helpers/userAgent'; @@ -64,6 +65,8 @@ console.timeEnd('get storage1'); */ const vh = (setViewportVH && !rootScope.default.overlayIsActive ? w.height || w.innerHeight : window.innerHeight) * 0.01; if(lastVH === vh) { return; + } else if(lastVH < vh) { + blurActiveElement(); // (Android) fix blur when keyboard is being closed } lastVH = vh; diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 236e773c..4cd81020 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -16,7 +16,6 @@ import { isSafari } from "../../helpers/userAgent"; import { logger, LogTypes } from "../logger"; import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; -import { attachClickEvent, positionElementByIndex, replaceContent } from "../../helpers/dom"; import apiUpdatesManager from "./apiUpdatesManager"; import appPeersManager from './appPeersManager'; import appImManager from "./appImManager"; @@ -40,6 +39,9 @@ import lottieLoader from "../lottieLoader"; import { wrapLocalSticker } from "../../components/wrappers"; import AppEditFolderTab from "../../components/sidebarLeft/tabs/editFolder"; import appSidebarLeft from "../../components/sidebarLeft"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import positionElementByIndex from "../../helpers/dom/positionElementByIndex"; +import replaceContent from "../../helpers/dom/replaceContent"; export type DialogDom = { avatarEl: AvatarElement, diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index bce20395..b0e7d982 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -24,7 +24,6 @@ import appPhotosManager from './appPhotosManager'; import appProfileManager from './appProfileManager'; import appStickersManager from './appStickersManager'; import appWebPagesManager from './appWebPagesManager'; -import { blurActiveElement, cancelEvent, disableTransition, placeCaretAtEnd, replaceContent, whichChild } from '../../helpers/dom'; import PopupNewMedia from '../../components/popups/newMedia'; import MarkupTooltip from '../../components/chat/markupTooltip'; import { isTouchSupported } from '../../helpers/touchSupport'; @@ -51,6 +50,12 @@ import { getFilesFromEvent } from '../../helpers/files'; import PeerTitle from '../../components/peerTitle'; import PopupPeer from '../../components/popups/peer'; import { SliceEnd } from '../../helpers/slicedArray'; +import blurActiveElement from '../../helpers/dom/blurActiveElement'; +import { cancelEvent } from '../../helpers/dom/cancelEvent'; +import disableTransition from '../../helpers/dom/disableTransition'; +import placeCaretAtEnd from '../../helpers/dom/placeCaretAtEnd'; +import replaceContent from '../../helpers/dom/replaceContent'; +import whichChild from '../../helpers/dom/whichChild'; //console.log('appImManager included33!'); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index cf019688..8c3aa668 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -49,7 +49,8 @@ import SlicedArray, { Slice, SliceEnd } from "../../helpers/slicedArray"; import appNotificationsManager, { NotifyOptions } from "./appNotificationsManager"; import PeerTitle from "../../components/peerTitle"; import { forEachReverse } from "../../helpers/array"; -import { htmlToDocumentFragment, htmlToSpan } from "../../helpers/dom"; +import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment"; +import htmlToSpan from "../../helpers/dom/htmlToSpan"; //console.trace('include'); // TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет @@ -1604,8 +1605,8 @@ export class AppMessagesManager { return !message.pFlags.out && readMaxId < historyStorage.maxId ? readMaxId : 0; } else { const message = this.getMessageByPeer(peerId, historyStorage.maxId); - const maxId = peerId > 0 ? Math.max(historyStorage.readMaxId, historyStorage.readOutboxMaxId) : historyStorage.readMaxId; - return !message.pFlags.out && maxId < historyStorage.maxId ? maxId : 0; + const readMaxId = peerId > 0 ? Math.max(historyStorage.readMaxId, historyStorage.readOutboxMaxId) : historyStorage.readMaxId; + return !message.pFlags.out && readMaxId < historyStorage.maxId ? readMaxId : 0; } } @@ -3417,7 +3418,7 @@ export class AppMessagesManager { // TODO: cancel notification by peer when this function is being called public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) { - return Promise.resolve(); + // return Promise.resolve(); // console.trace('start read') this.log('readHistory:', peerId, maxId, threadId); if(!this.getReadMaxIdIfUnread(peerId, threadId) && !force) { diff --git a/src/lib/appManagers/appPhotosManager.ts b/src/lib/appManagers/appPhotosManager.ts index 2e47153b..18f974b6 100644 --- a/src/lib/appManagers/appPhotosManager.ts +++ b/src/lib/appManagers/appPhotosManager.ts @@ -18,13 +18,13 @@ import { isSafari } from "../../helpers/userAgent"; import { InputFileLocation, InputMedia, Photo, PhotoSize, PhotosPhotos } from "../../layer"; import apiManager from "../mtproto/mtprotoworker"; import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase"; -import { calcImageInBox } from "../../helpers/dom"; import { MyDocument } from "./appDocsManager"; import appDownloadManager from "./appDownloadManager"; import appUsersManager from "./appUsersManager"; import blur from "../../helpers/blur"; import { MOUNT_CLASS_TO } from "../../config/debug"; import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; +import calcImageInBox from "../../helpers/calcImageInBox"; export type MyPhoto = Photo.photo; diff --git a/src/lib/appManagers/appProfileManager.ts b/src/lib/appManagers/appProfileManager.ts index bb48b2dd..408fc036 100644 --- a/src/lib/appManagers/appProfileManager.ts +++ b/src/lib/appManagers/appProfileManager.ts @@ -11,8 +11,8 @@ import { MOUNT_CLASS_TO } from "../../config/debug"; import { tsNow } from "../../helpers/date"; -import { replaceContent } from "../../helpers/dom"; import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; +import replaceContent from "../../helpers/dom/replaceContent"; import sequentialDom from "../../helpers/sequentialDom"; import { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, Update, UserFull, UserProfilePhoto } from "../../layer"; //import apiManager from '../mtproto/apiManager'; diff --git a/src/lib/mediaPlayer.ts b/src/lib/mediaPlayer.ts index 1ce46442..a91953b5 100644 --- a/src/lib/mediaPlayer.ts +++ b/src/lib/mediaPlayer.ts @@ -4,12 +4,12 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { cancelEvent } from "../helpers/dom"; import appMediaPlaybackController from "../components/appMediaPlaybackController"; import { isAppleMobile } from "../helpers/userAgent"; import { isTouchSupported } from "../helpers/touchSupport"; import RangeSelector from "../components/rangeSelector"; import { onVideoLoad } from "../helpers/files"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; export class MediaProgressLine extends RangeSelector { private filledLoad: HTMLDivElement; diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 94736b72..751ac112 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -121,7 +121,9 @@ export class RootScope extends EventListenerBase<{ private _overlayIsActive: boolean = false; public myId = 0; public idle = { - isIDLE: true + isIDLE: true, + focusPromise: Promise.resolve(), + focusResolve: () => {} }; public connectionStatus: {[name: string]: ConnectionStatusChange} = {}; public settings: State['settings']; @@ -143,6 +145,16 @@ export class RootScope extends EventListenerBase<{ const status = e; this.connectionStatus[e.name] = status; }); + + this.on('idle', (isIDLE) => { + if(isIDLE) { + this.idle.focusPromise = new Promise((resolve) => { + this.idle.focusResolve = resolve; + }); + } else { + this.idle.focusResolve(); + } + }); } public setThemeListener() { diff --git a/src/pages/pageAuthCode.ts b/src/pages/pageAuthCode.ts index 692ec05d..b500946c 100644 --- a/src/pages/pageAuthCode.ts +++ b/src/pages/pageAuthCode.ts @@ -15,9 +15,9 @@ import pageSignIn from './pageSignIn'; import pageSignUp from './pageSignUp'; 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'; +import replaceContent from '../helpers/dom/replaceContent'; let authCode: AuthSentCode.authSentCode = null; diff --git a/src/pages/pageIm.ts b/src/pages/pageIm.ts index 1dbdcdff..155c5dae 100644 --- a/src/pages/pageIm.ts +++ b/src/pages/pageIm.ts @@ -6,7 +6,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { blurActiveElement } from "../helpers/dom"; +import blurActiveElement from "../helpers/dom/blurActiveElement"; import appStateManager from "../lib/appManagers/appStateManager"; import I18n from "../lib/langPack"; import Page from "./page"; diff --git a/src/pages/pagePassword.ts b/src/pages/pagePassword.ts index 9b29a5c9..3a15163d 100644 --- a/src/pages/pagePassword.ts +++ b/src/pages/pagePassword.ts @@ -17,7 +17,11 @@ import PasswordMonkey from '../components/monkeys/password'; import RichTextProcessor from '../lib/richtextprocessor'; import I18n from '../lib/langPack'; import LoginPage from './loginPage'; -import { attachClickEvent, cancelEvent, htmlToSpan, replaceContent, toggleDisability } from '../helpers/dom'; +import { cancelEvent } from '../helpers/dom/cancelEvent'; +import { attachClickEvent } from '../helpers/dom/clickEvent'; +import htmlToSpan from '../helpers/dom/htmlToSpan'; +import replaceContent from '../helpers/dom/replaceContent'; +import toggleDisability from '../helpers/dom/toggleDisability'; const TEST = false; let passwordInput: HTMLInputElement; diff --git a/src/pages/pageSignIn.ts b/src/pages/pageSignIn.ts index c021a5ef..3e192d33 100644 --- a/src/pages/pageSignIn.ts +++ b/src/pages/pageSignIn.ts @@ -10,7 +10,6 @@ import Countries, { Country as _Country } from "../countries"; import appStateManager from "../lib/appManagers/appStateManager"; import apiManager from "../lib/mtproto/mtprotoworker"; import { RichTextProcessor } from '../lib/richtextprocessor'; -import { attachClickEvent, cancelEvent, replaceContent, toggleDisability } from "../helpers/dom"; import Page from "./page"; import InputField from "../components/inputField"; import CheckboxField from "../components/checkboxField"; @@ -30,6 +29,10 @@ import AppStorage from "../lib/storage"; import CacheStorageController from "../lib/cacheStorage"; import pageSignQR from "./pageSignQR"; import getLanguageChangeButton from "../components/languageChangeButton"; +import { cancelEvent } from "../helpers/dom/cancelEvent"; +import { attachClickEvent } from "../helpers/dom/clickEvent"; +import replaceContent from "../helpers/dom/replaceContent"; +import toggleDisability from "../helpers/dom/toggleDisability"; type Country = _Country & { li?: HTMLLIElement[] diff --git a/src/pages/pageSignUp.ts b/src/pages/pageSignUp.ts index 59ab1333..83e87bb5 100644 --- a/src/pages/pageSignUp.ts +++ b/src/pages/pageSignUp.ts @@ -11,7 +11,6 @@ import Button from '../components/button'; import InputField from '../components/inputField'; import { putPreloader } from '../components/misc'; import PopupAvatar from '../components/popups/avatar'; -import { blurActiveElement, replaceContent } from '../helpers/dom'; import appStateManager from '../lib/appManagers/appStateManager'; import I18n, { i18n } from '../lib/langPack'; //import apiManager from '../lib/mtproto/apiManager'; @@ -20,6 +19,8 @@ import RichTextProcessor from '../lib/richtextprocessor'; import LoginPage from './loginPage'; import Page from './page'; import pageIm from './pageIm'; +import blurActiveElement from '../helpers/dom/blurActiveElement'; +import replaceContent from '../helpers/dom/replaceContent'; let authCode: AuthState.signUp['authCode'] = null; diff --git a/src/pages/pagesManager.ts b/src/pages/pagesManager.ts index 2d191892..fa422f11 100644 --- a/src/pages/pagesManager.ts +++ b/src/pages/pagesManager.ts @@ -5,11 +5,11 @@ */ import Page from "./page"; -import { whichChild } from "../helpers/dom"; import lottieLoader from "../lib/lottieLoader"; import { horizontalMenu } from "../components/horizontalMenu"; import { MOUNT_CLASS_TO } from "../config/debug"; import fastSmoothScroll from "../helpers/fastSmoothScroll"; +import whichChild from "../helpers/dom/whichChild"; class PagesManager { private pageId = -1;