From 0c875ccbaf54a3da82b199ee2b8ef2ec241bf818 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Thu, 18 Feb 2021 16:11:16 +0400 Subject: [PATCH] 222 --- src/components/appNavigationController.ts | 27 ++++++++++++++--- src/components/chat/bubbles.ts | 5 ++-- src/components/chat/input.ts | 4 +-- src/components/popups/datePicker.ts | 2 +- src/components/popups/index.ts | 35 ++++++++++++++--------- src/components/popups/newMedia.ts | 2 +- src/helpers/userAgent.ts | 4 ++- 7 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/components/appNavigationController.ts b/src/components/appNavigationController.ts index 4611e207..d0ee48be 100644 --- a/src/components/appNavigationController.ts +++ b/src/components/appNavigationController.ts @@ -1,9 +1,12 @@ import { MOUNT_CLASS_TO } from "../config/debug"; -import { isSafari } from "../helpers/userAgent"; +import { isSafari, isAppleMobile } from "../helpers/userAgent"; +import { cancelEvent } from "../helpers/dom"; export type NavigationItem = { type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' | 'esg', - onPop: (canAnimate: boolean) => boolean | void + onPop: (canAnimate: boolean) => boolean | void, + onEscape?: () => boolean, + noHistory?: boolean, }; export class AppNavigationController { @@ -26,7 +29,7 @@ export class AppNavigationController { return; } - const good = item.onPop(isSafari ? false : undefined); + const good = item.onPop(isSafari && isAppleMobile ? false : undefined); console.log('[NC]: popstate, navigation:', item, this.navigations); if(good === false) { this.pushItem(item); @@ -35,6 +38,15 @@ export class AppNavigationController { //this.pushState(); // * prevent adding forward arrow }); + window.addEventListener('keydown', (e) => { + const item = this.navigations[this.navigations.length - 1]; + if(!item) return; + if(e.key === 'Escape' && (item.onEscape ? item.onEscape() : true)) { + cancelEvent(e); + this.back(); + } + }, {capture: true}); + this.pushState(); // * push init state } @@ -45,7 +57,10 @@ export class AppNavigationController { public pushItem(item: NavigationItem) { this.navigations.push(item); console.log('[NC]: pushstate', item, this.navigations); - this.pushState(); + + if(!item.noHistory) { + this.pushState(); + } } private pushState() { @@ -55,6 +70,10 @@ export class AppNavigationController { public replaceState() { history.replaceState(this.id, ''); } + + public removeItem(item: NavigationItem) { + this.navigations.findAndSplice(i => i === item); + } } const appNavigationController = new AppNavigationController(); diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index a5bdcd31..b158460c 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -1654,7 +1654,8 @@ export default class ChatBubbles { public setBubblePosition(bubble: HTMLElement, message: any, reverse: boolean) { const dateMessage = this.getDateContainerByMessage(message, reverse); if(this.chat.type === 'scheduled' || this.chat.type === 'pinned') { - let children = Array.from(dateMessage.container.children).slice(2) as HTMLElement[]; + const offset = this.stickyIntersector ? 2 : 1; + let children = Array.from(dateMessage.container.children).slice(offset) as HTMLElement[]; let i = 0, foundMidOnSameTimestamp = 0; for(; i < children.length; ++i) { const t = children[i]; @@ -1671,7 +1672,7 @@ export default class ChatBubbles { } // * 1 for date, 1 for date sentinel - let index = this.stickyIntersector ? 2 + i : 1 + i; + let index = offset + i; /* if(bubble.parentElement) { // * if already mounted const currentIndex = whichChild(bubble); if(index > currentIndex) { diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index a9c55b9c..cdd26ca2 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -556,7 +556,7 @@ export default class ChatInput { } public saveDraft() { - if(!this.chat.peerId || this.editMsgId) return; + if(!this.chat.peerId || this.editMsgId || this.chat.type === 'scheduled') return; const entities: MessageEntity[] = []; const str = getRichValue(this.messageInputField.input, entities); @@ -604,7 +604,7 @@ export default class ChatInput { } public setDraft(draft?: MyDraftMessage, fromUpdate = true) { - if(!isInputEmpty(this.messageInput)) return false; + if(!isInputEmpty(this.messageInput) || this.chat.type === 'scheduled') return false; if(!draft) { draft = this.appDraftsManager.getDraft(this.chat.peerId, this.chat.threadId); diff --git a/src/components/popups/datePicker.ts b/src/components/popups/datePicker.ts index 33c523ff..a9ca1bfa 100644 --- a/src/components/popups/datePicker.ts +++ b/src/components/popups/datePicker.ts @@ -141,7 +141,7 @@ export default class PopupDatePicker extends PopupElement { this.onPick(this.selectedDate.getTime() / 1000 | 0); } - this.destroy(); + this.hide(); }, {once: true}); this.body.append(this.timeDiv); diff --git a/src/components/popups/index.ts b/src/components/popups/index.ts index 52d57e0a..ebe122cc 100644 --- a/src/components/popups/index.ts +++ b/src/components/popups/index.ts @@ -2,6 +2,8 @@ import rootScope from "../../lib/rootScope"; import { blurActiveElement, cancelEvent, findUpClassName } from "../../helpers/dom"; import { ripple } from "../ripple"; import animationIntersector from "../animationIntersector"; +import appNavigationController, { NavigationItem } from "../appNavigationController"; +import { isMobileSafari, isSafari } from "../../helpers/userAgent"; export type PopupOptions = Partial<{closable: true, overlayClosable: true, withConfirm: string, body: true}>; export default class PopupElement { @@ -17,6 +19,8 @@ export default class PopupElement { protected onCloseAfterTimeout: () => void; protected onEscape: () => boolean = () => true; + protected navigationItem: NavigationItem; + constructor(className: string, buttons?: Array, options: PopupOptions = {}) { this.element.classList.add('popup'); this.element.className = 'popup' + (className ? ' ' + className : ''); @@ -33,7 +37,7 @@ export default class PopupElement { //ripple(this.closeBtn); this.header.prepend(this.btnClose); - this.btnClose.addEventListener('click', this.destroy, {once: true}); + this.btnClose.addEventListener('click', this.hide, {once: true}); if(options.overlayClosable) { const onOverlayClick = (e: MouseEvent) => { @@ -46,8 +50,6 @@ export default class PopupElement { } } - window.addEventListener('keydown', this._onKeyDown, {capture: true}); - if(options.withConfirm) { this.btnConfirm = document.createElement('button'); this.btnConfirm.classList.add('btn-primary'); @@ -98,14 +100,15 @@ export default class PopupElement { this.element.append(this.container); } - private _onKeyDown = (e: KeyboardEvent) => { - if(e.key === 'Escape' && this.onEscape()) { - cancelEvent(e); - this.destroy(); - } - }; - public show() { + this.navigationItem = { + type: 'popup', + onPop: this.destroy, + onEscape: this.onEscape + }; + + appNavigationController.pushItem(this.navigationItem); + blurActiveElement(); // * hide mobile keyboard document.body.append(this.element); void this.element.offsetWidth; // reflow @@ -114,15 +117,21 @@ export default class PopupElement { animationIntersector.checkAnimations(true); } - public destroy = () => { + public hide = () => { + appNavigationController.back(); + }; + + private destroy = () => { this.onClose && this.onClose(); this.element.classList.add('hiding'); this.element.classList.remove('active'); - window.removeEventListener('keydown', this._onKeyDown, {capture: true}); - if(this.btnClose) this.btnClose.removeEventListener('click', this.destroy); + if(this.btnClose) this.btnClose.removeEventListener('click', this.hide); rootScope.overlayIsActive = false; + appNavigationController.removeItem(this.navigationItem); + this.navigationItem = undefined; + setTimeout(() => { this.element.remove(); this.onCloseAfterTimeout && this.onCloseAfterTimeout(); diff --git a/src/components/popups/newMedia.ts b/src/components/popups/newMedia.ts index 26b7f8b2..058d4bde 100644 --- a/src/components/popups/newMedia.ts +++ b/src/components/popups/newMedia.ts @@ -142,7 +142,7 @@ export default class PopupNewMedia extends PopupElement { return; } - this.destroy(); + this.hide(); const willAttach = this.willAttach; willAttach.isMedia = willAttach.type === 'media' ? true : undefined; diff --git a/src/helpers/userAgent.ts b/src/helpers/userAgent.ts index d27d40dc..fef74821 100644 --- a/src/helpers/userAgent.ts +++ b/src/helpers/userAgent.ts @@ -18,4 +18,6 @@ export const isAppleMobile = (/iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) && !ctx.MSStream; -export const isSafari = !!('safari' in ctx) || !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))))/* || true */; \ No newline at end of file +export const isSafari = !!('safari' in ctx) || !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))))/* || true */; + +export const isMobileSafari = isSafari && isAppleMobile;