From cbd00b48160b45a0fc2da2bcd9ad7b7e814a17a5 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Mon, 20 Dec 2021 08:25:52 +0400 Subject: [PATCH] Mobile search fixes --- src/components/appNavigationController.ts | 7 +- src/components/chat/chat.ts | 7 +- src/components/chat/search.ts | 89 +++++++++++++++-------- src/scss/partials/_chatSearch.scss | 2 + src/scss/partials/_chatTopbar.scss | 18 +++-- 5 files changed, 87 insertions(+), 36 deletions(-) diff --git a/src/components/appNavigationController.ts b/src/components/appNavigationController.ts index 6ef011a2..fbd3274a 100644 --- a/src/components/appNavigationController.ts +++ b/src/components/appNavigationController.ts @@ -15,7 +15,8 @@ import isSwipingBackSafari from "../helpers/dom/isSwipingBackSafari"; export type NavigationItem = { type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' | - 'esg' | 'multiselect' | 'input-helper' | 'autocomplete-helper' | 'markup' | 'global-search' | 'voice', + 'esg' | 'multiselect' | 'input-helper' | 'autocomplete-helper' | 'markup' | + 'global-search' | 'voice' | 'mobile-search', onPop: (canAnimate: boolean) => boolean | void, onEscape?: () => boolean, noHistory?: boolean, @@ -187,6 +188,10 @@ export class AppNavigationController { } public removeItem(item: NavigationItem) { + if(!item) { + return; + } + indexOfAndSplice(this.navigations, item); } diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 5b72c2c7..0c9d4bef 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -55,6 +55,7 @@ export default class Chat extends EventListenerBase<{ public input: ChatInput; public selection: ChatSelection; public contextMenu: ChatContextMenu; + public search: ChatSearch; public wasAlreadyUsed = false; // public initPeerId = 0; @@ -396,7 +397,11 @@ export default class Chat extends EventListenerBase<{ if(!this.peerId) return; if(mediaSizes.isMobile) { - new ChatSearch(this.topbar, this, query); + if(!this.search) { + this.search = new ChatSearch(this.topbar, this, query); + } else { + this.search.setQuery(query); + } } else { let tab = appSidebarRight.getTab(AppPrivateSearchTab); if(!tab) { diff --git a/src/components/chat/search.ts b/src/components/chat/search.ts index 566002e1..7e49d8be 100644 --- a/src/components/chat/search.ts +++ b/src/components/chat/search.ts @@ -15,6 +15,10 @@ import { cancelEvent } from "../../helpers/dom/cancelEvent"; import whichChild from "../../helpers/dom/whichChild"; import replaceContent from "../../helpers/dom/replaceContent"; import { i18n } from "../../lib/langPack"; +import ListenerSetter from "../../helpers/listenerSetter"; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import appNavigationController, { NavigationItem } from "../appNavigationController"; +import { IS_MOBILE_SAFARI } from "../../environment/userAgent"; export default class ChatSearch { private element: HTMLElement; @@ -36,28 +40,26 @@ export default class ChatSearch { private foundCount = 0; private selectedIndex = 0; private setPeerPromise: Promise; + private listenerSetter: ListenerSetter; + private navigationItem: NavigationItem; - constructor(private topbar: ChatTopbar, private chat: Chat, private query?: string) { + constructor(private topbar: ChatTopbar, private chat: Chat, query?: string) { this.element = document.createElement('div'); this.element.classList.add('sidebar-header', 'chat-search', 'chatlist-container'); this.backBtn = document.createElement('button'); this.backBtn.classList.add('btn-icon', 'tgico-left', 'sidebar-close-button'); ripple(this.backBtn); + + const listenerSetter = this.listenerSetter = new ListenerSetter(); + + const attachClick = (element: HTMLElement, callback: (e: MouseEvent) => void) => { + attachClickEvent(element, callback, {listenerSetter}); + }; - this.backBtn.addEventListener('click', () => { - this.topbar.container.classList.remove('hide-pinned'); - this.element.remove(); - this.inputSearch.remove(); - this.results.remove(); - this.footer.remove(); - this.footer.removeEventListener('click', this.onFooterClick); - this.dateBtn.removeEventListener('click', this.onDateClick); - this.upBtn.removeEventListener('click', this.onUpClick); - this.downBtn.removeEventListener('click', this.onDownClick); - this.searchGroup.list.removeEventListener('click', this.onResultsClick); - this.chat.bubbles.bubblesContainer.classList.remove('search-results-active'); - }, {once: true}); + attachClick(this.backBtn, () => { + this.destroy(); + }); this.inputSearch = new InputSearch('Search'); @@ -66,7 +68,7 @@ export default class ChatSearch { this.results.classList.add('chat-search-results', 'chatlist-container'); this.searchGroup = new SearchGroup(false, 'messages', undefined, '', false); - this.searchGroup.list.addEventListener('click', this.onResultsClick); + attachClick(this.searchGroup.list, this.onResultsClick); this.appSearch = new AppSearch(this.results, this.inputSearch, { messages: this.searchGroup @@ -74,7 +76,7 @@ export default class ChatSearch { this.foundCount = count; if(!this.foundCount) { - this.foundCountEl.replaceWith(this.inputSearch.value ? i18n('NoResult') : ''); + replaceContent(this.foundCountEl, this.inputSearch.value ? i18n('NoResult') : ''); this.results.classList.remove('active'); this.chat.bubbles.bubblesContainer.classList.remove('search-results-active'); this.upBtn.setAttribute('disabled', 'true'); @@ -92,7 +94,7 @@ export default class ChatSearch { this.footer = document.createElement('div'); this.footer.classList.add('chat-search-footer'); - this.footer.addEventListener('click', this.onFooterClick); + attachClick(this.footer, this.onFooterClick); ripple(this.footer); this.foundCountEl = document.createElement('span'); @@ -112,9 +114,9 @@ export default class ChatSearch { this.upBtn.setAttribute('disabled', 'true'); this.downBtn.setAttribute('disabled', 'true'); - this.dateBtn.addEventListener('click', this.onDateClick); - this.upBtn.addEventListener('click', this.onUpClick); - this.downBtn.addEventListener('click', this.onDownClick); + attachClick(this.dateBtn, this.onDateClick); + attachClick(this.upBtn, this.onUpClick); + attachClick(this.downBtn, this.onDownClick); this.controls.append(this.upBtn, this.downBtn); this.footer.append(this.foundCountEl, this.dateBtn, this.controls); @@ -129,15 +131,44 @@ export default class ChatSearch { this.inputSearch.input.focus(); - query && (this.inputSearch.inputField.value = query); + if(query) { + this.setQuery(query); + } + + if(!IS_MOBILE_SAFARI) { + this.navigationItem = { + type: 'mobile-search', + onPop: () => { + this.destroy(); + } + }; + + appNavigationController.pushItem(this.navigationItem); + } } - onDateClick = (e: MouseEvent) => { + public destroy() { + this.topbar.container.classList.remove('hide-pinned'); + this.element.remove(); + this.inputSearch.remove(); + this.results.remove(); + this.footer.remove(); + this.listenerSetter.removeAll(); + this.chat.bubbles.bubblesContainer.classList.remove('search-results-active'); + this.chat.search = undefined; + appNavigationController.removeItem(this.navigationItem); + } + + public setQuery(query: string) { + this.inputSearch.inputField.value = query; + } + + private onDateClick = (e: MouseEvent) => { cancelEvent(e); new PopupDatePicker(new Date(), this.chat.bubbles.onDatePick).show(); }; - selectResult = (elem: HTMLElement) => { + private selectResult(elem: HTMLElement) { if(this.setPeerPromise) return this.setPeerPromise; const peerId = elem.dataset.peerId.toPeerId(); @@ -172,29 +203,29 @@ export default class ChatSearch { }).finally(() => { this.setPeerPromise = null; }); - }; + } - onResultsClick = (e: MouseEvent) => { + private onResultsClick = (e: MouseEvent) => { const target = findUpTag(e.target, 'LI'); if(target) { this.selectResult(target); } }; - onFooterClick = (e: MouseEvent) => { + private onFooterClick = (e: MouseEvent) => { if(this.foundCount) { this.chat.bubbles.bubblesContainer.classList.toggle('search-results-active'); this.results.classList.toggle('active'); } }; - onUpClick = (e: MouseEvent) => { + private onUpClick = (e: MouseEvent) => { cancelEvent(e); this.selectResult(this.searchGroup.list.children[this.selectedIndex + 1] as HTMLElement); }; - onDownClick = (e: MouseEvent) => { + private onDownClick = (e: MouseEvent) => { cancelEvent(e); this.selectResult(this.searchGroup.list.children[this.selectedIndex - 1] as HTMLElement); }; -} \ No newline at end of file +} diff --git a/src/scss/partials/_chatSearch.scss b/src/scss/partials/_chatSearch.scss index c390b8ec..069ced6e 100644 --- a/src/scss/partials/_chatSearch.scss +++ b/src/scss/partials/_chatSearch.scss @@ -7,6 +7,8 @@ .chat-search { position: absolute !important; top: 0; + right: 0; + left: 0; z-index: 2; &-footer { diff --git a/src/scss/partials/_chatTopbar.scss b/src/scss/partials/_chatTopbar.scss index 6e559778..2f1020a6 100644 --- a/src/scss/partials/_chatTopbar.scss +++ b/src/scss/partials/_chatTopbar.scss @@ -299,11 +299,19 @@ body.is-right-column-shown { } } - &.hide-pinned + .bubbles { - .bubbles-inner { - margin-bottom: .25rem; - } - } + &.hide-pinned { + --pinned-floating-height: 0px; + + .pinned-container { + display: none; + } + + & + .bubbles { + .bubbles-inner { + margin-bottom: .25rem; + } + } + } .chat:not(.type-chat) & { .content {