From 275bd4f77dbea9caa279d46445109afc4cfbfdbd Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Tue, 26 Jan 2021 02:40:53 +0200 Subject: [PATCH] Close search, contacts on list click --- src/components/appSearch.ts | 4 +- src/components/appSearchSuper..ts | 3 +- src/components/chat/bubbles.ts | 84 +++++++++++++++------ src/components/chat/chat.ts | 5 +- src/components/chat/pinnedMessage.ts | 7 +- src/components/sidebarLeft/index.ts | 12 ++- src/components/sidebarLeft/tabs/contacts.ts | 7 +- src/helpers/dom.ts | 8 +- src/lib/appManagers/appImManager.ts | 34 ++++++++- src/lib/appManagers/appStateManager.ts | 1 - src/lib/sessionStorage.ts | 12 ++- src/scss/partials/_chatBubble.scss | 4 + 12 files changed, 134 insertions(+), 47 deletions(-) diff --git a/src/components/appSearch.ts b/src/components/appSearch.ts index cbc7b2a8..f0fb1144 100644 --- a/src/components/appSearch.ts +++ b/src/components/appSearch.ts @@ -8,7 +8,7 @@ export class SearchGroup { nameEl: HTMLDivElement; list: HTMLUListElement; - constructor(public name: string, public type: string, private clearable = true, className?: string, clickable = true, public autonomous = true) { + constructor(public name: string, public type: string, private clearable = true, className?: string, clickable = true, public autonomous = true, public onFound?: () => void) { this.list = document.createElement('ul'); this.container = document.createElement('div'); if(className) this.container.className = className; @@ -25,7 +25,7 @@ export class SearchGroup { this.container.style.display = 'none'; if(clickable) { - appDialogsManager.setListClickListener(this.list, undefined, undefined, autonomous); + appDialogsManager.setListClickListener(this.list, onFound, undefined, autonomous); } } diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index e116bf59..93412006 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -22,7 +22,6 @@ import { ripple } from "./ripple"; import Scrollable, { ScrollableX } from "./scrollable"; import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers"; import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck"; -import { p } from "../mock/srp"; const testScroll = false; @@ -706,7 +705,7 @@ export default class AppSearchSuper { drawStatus: false, meAsSaved: true, avatarSize: 48, - autonomous: false + autonomous: true }); dom.lastMessageSpan.innerText = peerId > 0 ? appUsersManager.getUserStatusString(peerId) : appChatsManager.getChatMembersString(peerId); diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 73e4ad2a..823b41f5 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -1,12 +1,12 @@ -import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager"; -import type { AppMessagesManager, Dialog, HistoryResult, MyMessage } from "../../lib/appManagers/appMessagesManager"; -import type { AppSidebarRight } from "../sidebarRight"; +import { AppImManager, CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager"; +import type { AppMessagesManager, HistoryResult, 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"; import type { AppPhotosManager } from "../../lib/appManagers/appPhotosManager"; import type { AppDocsManager } from "../../lib/appManagers/appDocsManager"; import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; +import type sessionStorage from '../../lib/sessionStorage'; import { findUpClassName, cancelEvent, findUpTag, whichChild, getElementByPoint, attachClickEvent, positionElementByIndex } from "../../helpers/dom"; import { getObjectKeysAndSort } from "../../helpers/object"; import { isTouchSupported } from "../../helpers/touchSupport"; @@ -24,7 +24,7 @@ import animationIntersector from "../animationIntersector"; import { months } from "../../helpers/date"; import RichTextProcessor from "../../lib/richtextprocessor"; import mediaSizes from "../../helpers/mediaSizes"; -import { isAndroid, isApple, isSafari, isAppleMobile } from "../../helpers/userAgent"; +import { isAndroid, isApple, isSafari } from "../../helpers/userAgent"; import { langPack } from "../../lib/langPack"; import AvatarElement from "../avatar"; import { formatPhoneNumber } from "../misc"; @@ -37,13 +37,12 @@ import Chat from "./chat"; import ListenerSetter from "../../helpers/listenerSetter"; import PollElement from "../poll"; import AudioElement from "../audio"; -import { Message, MessageEntity, MessageReplies, MessageReplyHeader } from "../../layer"; -import { DEBUG, MOUNT_CLASS_TO, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; +import { Message, MessageEntity, MessageReplyHeader } from "../../layer"; +import { DEBUG, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; import { FocusDirection } from "../../helpers/fastSmoothScroll"; import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent } from "../../hooks/useHeavyAnimationCheck"; import { fastRaf } from "../../helpers/schedulers"; -import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise"; -import EventListenerBase from "../../helpers/eventListenerBase"; +import { deferredPromise } from "../../helpers/cancellablePromise"; const USE_MEDIA_TAILS = false; const IGNORE_ACTIONS = ['messageActionHistoryClear']; @@ -117,7 +116,7 @@ export default class ChatBubbles { public isFirstLoad = true; - 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) { + 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'); this.listenerSetter = new ListenerSetter(); @@ -889,6 +888,28 @@ export default class ChatBubbles { } } + public getBubbleByPoint(verticalSide: 'top' | 'bottom') { + let element = getElementByPoint(this.scrollable.container, verticalSide, 'center'); + /* if(element) { + if(element.classList.contains('bubbles-date-group')) { + const children = Array.from(element.children) as HTMLElement[]; + if(verticalSide === 'top') { + element = children[this.stickyIntersector ? 2 : 1]; + } else { + element = children[children.length - 1]; + } + } else { + element = findUpClassName(element, 'bubble'); + if(element && element.classList.contains('is-date')) { + element = element.nextElementSibling as HTMLElement; + } + } + } */ + if(element) element = findUpClassName(element, 'bubble'); + + return element; + } + public getGroupedBubble(groupId: string) { const group = this.appMessagesManager.groupedMessagesStorage[groupId]; for(const mid in group) { @@ -1325,14 +1346,22 @@ export default class ChatBubbles { topMessage = 0; } - let readMaxId = 0; - if(!isTarget && topMessage) { - readMaxId = this.appMessagesManager.getReadMaxIdIfUnread(peerId, this.chat.threadId); - if(/* dialog.unread_count */readMaxId && !samePeer) { - lastMsgId = readMaxId; - } else { - lastMsgId = topMessage; - //lastMsgID = topMessage; + let readMaxId = 0;//, savedPosition: ReturnType; + if(!isTarget) { + /* if(!samePeer) { + savedPosition = this.chat.appImManager.getChatSavedPosition(this.chat); + } + + if(savedPosition) { + lastMsgId = savedPosition.mid; + } else */if(topMessage) { + readMaxId = this.appMessagesManager.getReadMaxIdIfUnread(peerId, this.chat.threadId); + if(/* dialog.unread_count */readMaxId && !samePeer) { + lastMsgId = readMaxId; + } else { + lastMsgId = topMessage; + //lastMsgID = topMessage; + } } } @@ -1378,13 +1407,10 @@ export default class ChatBubbles { let maxBubbleId = 0; if(samePeer) { - let el = getElementByPoint(this.chat.bubbles.scrollable.container, 'bottom'); // ! this may not work if being called when chat is hidden + let el = this.getBubbleByPoint('bottom'); // ! this may not work if being called when chat is hidden //this.chat.log('[PM]: setCorrectIndex: get last element perf:', performance.now() - perf, el); if(el) { - el = findUpClassName(el, 'bubble'); - if(el) { // TODO: а что делать, если id будет -1, -2, -3? - maxBubbleId = +el.dataset.mid; - } + maxBubbleId = +el.dataset.mid; } if(maxBubbleId <= 0) { @@ -1438,7 +1464,19 @@ export default class ChatBubbles { this.lazyLoadQueue.unlock(); //if(dialog && lastMsgID && lastMsgID != topMessage && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) { - if((topMessage && isJump) || isTarget) { + /* if(savedPosition) { + const mountedByLastMsgId = this.getMountedBubble(lastMsgId); + let bubble: HTMLElement = mountedByLastMsgId?.bubble; + if(!bubble?.parentElement) { + bubble = this.findNextMountedBubbleByMsgId(lastMsgId); + } + + if(bubble) { + const top = bubble.getBoundingClientRect().top; + const distance = savedPosition.top - top; + this.scrollable.scrollTop += distance; + } + } else */if((topMessage && isJump) || isTarget) { const fromUp = maxBubbleId > 0 && (maxBubbleId < lastMsgId || lastMsgId < 0); const followingUnread = readMaxId === lastMsgId && !isTarget; if(!fromUp && samePeer) { diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 181addd7..0bc334f9 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -13,6 +13,7 @@ import type { AppWebPagesManager } from "../../lib/appManagers/appWebPagesManage import type { ApiManagerProxy } from "../../lib/mtproto/mtprotoworker"; import type { AppDraftsManager } from "../../lib/appManagers/appDraftsManager"; import type { ServerTimeManager } from "../../lib/mtproto/serverTimeManager"; +import type sessionStorage from '../../lib/sessionStorage'; import EventListenerBase from "../../helpers/eventListenerBase"; import { logger, LogLevels } from "../../lib/logger"; import rootScope from "../../lib/rootScope"; @@ -49,7 +50,7 @@ export default class Chat extends EventListenerBase<{ public type: ChatType = 'chat'; - constructor(public appImManager: AppImManager, public appChatsManager: AppChatsManager, public appDocsManager: AppDocsManager, public appInlineBotsManager: AppInlineBotsManager, public appMessagesManager: AppMessagesManager, public appPeersManager: AppPeersManager, public appPhotosManager: AppPhotosManager, public appProfileManager: AppProfileManager, public appStickersManager: AppStickersManager, public appUsersManager: AppUsersManager, public appWebPagesManager: AppWebPagesManager, public appPollsManager: AppPollsManager, public apiManager: ApiManagerProxy, public appDraftsManager: AppDraftsManager, public serverTimeManager: ServerTimeManager) { + constructor(public appImManager: AppImManager, public appChatsManager: AppChatsManager, public appDocsManager: AppDocsManager, public appInlineBotsManager: AppInlineBotsManager, public appMessagesManager: AppMessagesManager, public appPeersManager: AppPeersManager, public appPhotosManager: AppPhotosManager, public appProfileManager: AppProfileManager, public appStickersManager: AppStickersManager, public appUsersManager: AppUsersManager, public appWebPagesManager: AppWebPagesManager, public appPollsManager: AppPollsManager, public apiManager: ApiManagerProxy, public appDraftsManager: AppDraftsManager, public serverTimeManager: ServerTimeManager, public storage: typeof sessionStorage) { super(); this.container = document.createElement('div'); @@ -80,7 +81,7 @@ export default class Chat extends EventListenerBase<{ this.initPeerId = peerId; this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager); - this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appDocsManager, this.appPeersManager, this.appChatsManager); + this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appDocsManager, this.appPeersManager, this.appChatsManager, this.storage); this.input = new ChatInput(this, this.appMessagesManager, this.appDocsManager, this.appChatsManager, this.appPeersManager, this.appWebPagesManager, this.appImManager, this.appDraftsManager, this.serverTimeManager); this.selection = new ChatSelection(this, this.bubbles, this.input, this.appMessagesManager); this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appChatsManager, this.appPeersManager, this.appPollsManager); diff --git a/src/components/chat/pinnedMessage.ts b/src/components/chat/pinnedMessage.ts index c1f8103f..fcb93ccd 100644 --- a/src/components/chat/pinnedMessage.ts +++ b/src/components/chat/pinnedMessage.ts @@ -1,13 +1,12 @@ import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager"; import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; import type ChatTopbar from "./topbar"; -import { ScreenSize } from "../../helpers/mediaSizes"; import PopupPinMessage from "../popups/unpinMessage"; import PinnedContainer from "./pinnedContainer"; import PinnedMessageBorder from "./pinnedMessageBorder"; import ReplyContainer, { wrapReplyDivAndCaption } from "./replyContainer"; import rootScope from "../../lib/rootScope"; -import { attachClickEvent, cancelEvent, findUpClassName, getElementByPoint, handleScrollSideEvent } from "../../helpers/dom"; +import { attachClickEvent, cancelEvent, handleScrollSideEvent } from "../../helpers/dom"; import Chat from "./chat"; import ListenerSetter from "../../helpers/listenerSetter"; import ButtonIcon from "../buttonIcon"; @@ -336,11 +335,9 @@ export default class ChatPinnedMessage { } //const perf = performance.now(); - let el = getElementByPoint(this.chat.bubbles.scrollable.container, 'bottom'); + let el = this.chat.bubbles.getBubbleByPoint('bottom'); //this.chat.log('[PM]: setCorrectIndex: get last element perf:', performance.now() - perf, el); if(!el) return; - el = findUpClassName(el, 'bubble'); - if(!el) return; //return; diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index ddcecb74..6f0e7d1b 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -172,12 +172,16 @@ export class AppSidebarLeft extends SidebarSlider { const scrollable = new Scrollable(searchContainer); + const close = () => { + this.backBtn.click(); + }; + this.searchGroups = { - contacts: new SearchGroup('Chats', 'contacts'), - globalContacts: new SearchGroup('Global Search', 'contacts'), + contacts: new SearchGroup('Chats', 'contacts', undefined, undefined, undefined, undefined, close), + globalContacts: new SearchGroup('Global Search', 'contacts', undefined, undefined, undefined, undefined, close), messages: new SearchGroup('Messages', 'messages'), - people: new SearchGroup('', 'contacts', true, 'search-group-people', true, false), - recent: new SearchGroup('Recent', 'contacts', true, 'search-group-recent', true, false) + people: new SearchGroup('', 'contacts', true, 'search-group-people', true, false, close), + recent: new SearchGroup('Recent', 'contacts', true, 'search-group-recent', true, true, close) }; const searchSuper = this.searchSuper = new AppSearchSuper([{ diff --git a/src/components/sidebarLeft/tabs/contacts.ts b/src/components/sidebarLeft/tabs/contacts.ts index 1ee9901e..997ed096 100644 --- a/src/components/sidebarLeft/tabs/contacts.ts +++ b/src/components/sidebarLeft/tabs/contacts.ts @@ -21,7 +21,10 @@ export default class AppContactsTab implements SliderTab { this.container = document.getElementById('contacts-container'); this.list = this.container.querySelector('#contacts'); - appDialogsManager.setListClickListener(this.list); + appDialogsManager.setListClickListener(this.list, () => { + (this.container.querySelector('.sidebar-close-button') as HTMLElement).click(); + }, undefined, true); + this.scrollable = new Scrollable(this.list.parentElement); this.inputSearch = new InputSearch('Search', (value) => { @@ -95,7 +98,7 @@ export default class AppContactsTab implements SliderTab { container: this.list, drawStatus: false, avatarSize: 48, - autonomous: false + autonomous: true }); let status = appUsersManager.getUserStatusString(user.id); diff --git a/src/helpers/dom.ts b/src/helpers/dom.ts index b7c4c721..d213dae2 100644 --- a/src/helpers/dom.ts +++ b/src/helpers/dom.ts @@ -643,13 +643,15 @@ export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', } }; -export const getElementByPoint = (container: HTMLElement, verticalSide: 'top' | 'bottom'): HTMLElement => { +export const getElementByPoint = (container: HTMLElement, verticalSide: 'top' | 'bottom', horizontalSide: 'center' | 'left'): HTMLElement => { const rect = container.getBoundingClientRect(); - const x = Math.ceil(rect.left + ((rect.right - rect.left) / 2) + 1); - const y = verticalSide == 'bottom' ? Math.floor(rect.top + rect.height - 1) : Math.ceil(rect.top + 1); + const x = horizontalSide === 'center' ? Math.ceil(rect.left + ((rect.right - rect.left) / 2) + 1) : Math.ceil(rect.left + 1); + const y = verticalSide === 'bottom' ? Math.floor(rect.top + rect.height - 1) : Math.ceil(rect.top + 1); return document.elementFromPoint(x, y) as any; }; +MOUNT_CLASS_TO && (MOUNT_CLASS_TO.getElementByPoint = getElementByPoint); + export async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes = false): Promise { const files: any[] = []; diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index c6df5173..06d1c984 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -32,6 +32,7 @@ import lottieLoader from '../lottieLoader'; import useHeavyAnimationCheck, { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import appDraftsManager from './appDraftsManager'; import serverTimeManager from '../mtproto/serverTimeManager'; +import sessionStorage from '../sessionStorage'; //console.log('appImManager included33!'); @@ -152,8 +153,39 @@ export class AppImManager { animationIntersector.setOnlyOnePlayableGroup(''); animationIntersector.checkAnimations(false); }); + + /* rootScope.on('peer_changing', (chat) => { + this.saveChatPosition(chat); + }); + + sessionStorage.get('chatPositions').then((c) => { + sessionStorage.setToCache('chatPositions', c || {}); + }); */ } + /* public saveChatPosition(chat: Chat) { + const bubble = chat.bubbles.getBubbleByPoint('top'); + if(bubble) { + const top = bubble.getBoundingClientRect().top; + + this.log('saving position by bubble:', bubble, top); + + const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : ''); + + const chatPositions = sessionStorage.getFromCache('chatPositions'); + chatPositions[key] = { + mid: +bubble.dataset.mid, + top + }; + sessionStorage.set({chatPositions}); + } + } + + public getChatSavedPosition(chat: Chat) { + const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : ''); + return sessionStorage.getFromCache('chatPositions')[key]; + } */ + private setSettings() { document.documentElement.style.setProperty('--messages-text-size', rootScope.settings.messagesTextSize + 'px'); @@ -469,7 +501,7 @@ export class AppImManager { } private createNewChat() { - const chat = new Chat(this, appChatsManager, appDocsManager, appInlineBotsManager, appMessagesManager, appPeersManager, appPhotosManager, appProfileManager, appStickersManager, appUsersManager, appWebPagesManager, appPollsManager, apiManager, appDraftsManager, serverTimeManager); + const chat = new Chat(this, appChatsManager, appDocsManager, appInlineBotsManager, appMessagesManager, appPeersManager, appPhotosManager, appProfileManager, appStickersManager, appUsersManager, appWebPagesManager, appPollsManager, apiManager, appDraftsManager, serverTimeManager, sessionStorage); this.chats.push(chat); } diff --git a/src/lib/appManagers/appStateManager.ts b/src/lib/appManagers/appStateManager.ts index e64b750e..4fb9576f 100644 --- a/src/lib/appManagers/appStateManager.ts +++ b/src/lib/appManagers/appStateManager.ts @@ -1,5 +1,4 @@ import type { Dialog } from './appMessagesManager'; -import type { AppStickersManager } from './appStickersManager'; import { App, MOUNT_CLASS_TO, UserAuth } from '../mtproto/mtproto_config'; import EventListenerBase from '../../helpers/eventListenerBase'; import rootScope from '../rootScope'; diff --git a/src/lib/sessionStorage.ts b/src/lib/sessionStorage.ts index 0b65d7be..499e4e0d 100644 --- a/src/lib/sessionStorage.ts +++ b/src/lib/sessionStorage.ts @@ -1,6 +1,7 @@ +import type { AppImManager } from './appManagers/appImManager'; +import type { State } from './appManagers/appStateManager'; import { MOUNT_CLASS_TO } from './mtproto/mtproto_config'; import AppStorage from './storage'; -import { State } from './appManagers/appStateManager'; const sessionStorage = new AppStorage<{ dc: number, @@ -11,7 +12,14 @@ const sessionStorage = new AppStorage<{ dc4_auth_key: any, dc5_auth_key: any, max_seen_msg: number, - server_time_offset: number + server_time_offset: number, + + chatPositions: { + [peerId_threadId: string]: { + mid: number, + top: number + } + }, } & State>({ storeName: 'session' }); diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 4000ad0a..16074248 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -281,6 +281,10 @@ $bubble-margin: .25rem; opacity: .99999; // for safari pointer-events: none; + &:before, &:after { + display: none; + } + body.animation-level-0 & { transition: none; }