diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index 1f462e6d..7e37bba0 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -26,7 +26,6 @@ import { ripple } from "./ripple"; import Scrollable, { ScrollableX } from "./scrollable"; import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers"; import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck"; -import { isSafari } from "../helpers/userAgent"; import { LangPackKey, i18n } from "../lib/langPack"; import findUpClassName from "../helpers/dom/findUpClassName"; import { getMiddleware } from "../helpers/middleware"; @@ -39,6 +38,8 @@ import mediaSizes from "../helpers/mediaSizes"; import appImManager from "../lib/appManagers/appImManager"; import positionElementByIndex from "../helpers/dom/positionElementByIndex"; import cleanSearchText from "../helpers/cleanSearchText"; +import { isTouchSupported } from "../helpers/touchSupport"; +import handleTabSwipe from "../helpers/dom/handleTabSwipe"; //const testScroll = false; @@ -164,6 +165,13 @@ export default class AppSearchSuper { this.tabsContainer = document.createElement('div'); this.tabsContainer.classList.add('search-super-tabs-container', 'tabs-container'); + if(isTouchSupported) { + handleTabSwipe(this.tabsContainer, (next) => { + const prevId = this.selectTab.prevId(); + this.selectTab(next ? prevId + 1 : prevId - 1); + }); + } + for(const mediaTab of this.mediaTabs) { const container = document.createElement('div'); container.classList.add('search-super-container-' + mediaTab.type, 'tabs-tab'); diff --git a/src/components/misc.ts b/src/components/misc.ts index 8de36451..ff0bcdd8 100644 --- a/src/components/misc.ts +++ b/src/components/misc.ts @@ -12,6 +12,7 @@ import ListenerSetter from "../helpers/listenerSetter"; import mediaSizes from "../helpers/mediaSizes"; import { isTouchSupported } from "../helpers/touchSupport"; import { isApple, isMobileSafari } from "../helpers/userAgent"; +import rootScope from "../lib/rootScope"; import appNavigationController from "./appNavigationController"; export function putPreloader(elem: Element, returnDiv = false): HTMLElement { @@ -124,6 +125,8 @@ export const closeBtnMenu = () => { //openedMenu.previousElementSibling.remove(); // remove overlay if(menuOverlay) menuOverlay.remove(); openedMenu = null; + + rootScope.dispatchEvent('context_menu_toggle', false); } if(openedMenuOnClose) { @@ -205,6 +208,8 @@ export function openBtnMenu(menuElement: HTMLElement, onClose?: () => void) { // ! safari iOS doesn't handle window click event on overlay, idk why document.addEventListener(CLICK_EVENT_NAME, onClick); + + rootScope.dispatchEvent('context_menu_toggle', true); } const PADDING_TOP = 8; @@ -307,6 +312,20 @@ export function positionMenu({pageX, pageY}: MouseEvent | Touch, elem: HTMLEleme (side === 'center' ? side : (side === 'left' ? 'right' : 'left'))); } +let _cancelContextMenuOpening = false, _cancelContextMenuOpeningTimeout = 0; +export function cancelContextMenuOpening() { + if(_cancelContextMenuOpeningTimeout) { + clearTimeout(_cancelContextMenuOpeningTimeout); + } + + _cancelContextMenuOpeningTimeout = window.setTimeout(() => { + _cancelContextMenuOpeningTimeout = 0; + _cancelContextMenuOpening = false; + }, .4e3); + + _cancelContextMenuOpening = true; +} + export function attachContextMenuListener(element: HTMLElement, callback: (e: Touch | MouseEvent) => void, listenerSetter?: ListenerSetter) { const add = listenerSetter ? listenerSetter.add(element) : element.addEventListener.bind(element); const remove = listenerSetter ? listenerSetter.removeManual.bind(listenerSetter, element) : element.removeEventListener.bind(element); @@ -337,6 +356,11 @@ export function attachContextMenuListener(element: HTMLElement, callback: (e: To add('touchcancel', onCancel, options); timeout = window.setTimeout(() => { + if(_cancelContextMenuOpening) { + onCancel(); + return; + } + callback(e.touches[0]); onCancel(); diff --git a/src/components/swipeHandler.ts b/src/components/swipeHandler.ts index c7bd9633..b38823f6 100644 --- a/src/components/swipeHandler.ts +++ b/src/components/swipeHandler.ts @@ -7,6 +7,7 @@ import { cancelEvent } from "../helpers/dom/cancelEvent"; import { safeAssign } from "../helpers/object"; import { isTouchSupported } from "../helpers/touchSupport"; +import rootScope from "../lib/rootScope"; const getEvent = (e: TouchEvent | MouseEvent) => { return (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent; @@ -14,13 +15,19 @@ const getEvent = (e: TouchEvent | MouseEvent) => { const attachGlobalListenerTo = window; +let RESET_GLOBAL = false; +rootScope.addEventListener('context_menu_toggle', (visible) => { + RESET_GLOBAL = visible; +}); + export default class SwipeHandler { private element: HTMLElement; - private onSwipe: (xDiff: number, yDiff: number) => boolean | void; + private onSwipe: (xDiff: number, yDiff: number, e: TouchEvent | MouseEvent) => boolean | void; private verifyTouchTarget: (evt: TouchEvent | MouseEvent) => boolean; private onFirstSwipe: () => void; private onReset: () => void; private cursor: 'grabbing' | 'move' = 'grabbing'; + private cancelEvent = true; private hadMove = false; private xDown: number = null; @@ -32,7 +39,8 @@ export default class SwipeHandler { verifyTouchTarget?: SwipeHandler['verifyTouchTarget'], onFirstSwipe?: SwipeHandler['onFirstSwipe'], onReset?: SwipeHandler['onReset'], - cursor?: SwipeHandler['cursor'] + cursor?: SwipeHandler['cursor'], + cancelEvent?: SwipeHandler['cancelEvent'] }) { safeAssign(this, options); @@ -96,11 +104,14 @@ export default class SwipeHandler { }; handleMove = (_e: TouchEvent | MouseEvent) => { - if(this.xDown === null || this.yDown === null) { - return this.reset(); + if(this.xDown === null || this.yDown === null || RESET_GLOBAL) { + this.reset(); + return; } - cancelEvent(_e); + if(this.cancelEvent) { + cancelEvent(_e); + } const e = getEvent(_e); const xUp = e.clientX; @@ -140,7 +151,7 @@ export default class SwipeHandler { // } /* reset values */ - const onSwipeResult = this.onSwipe(xDiff, yDiff); + const onSwipeResult = this.onSwipe(xDiff, yDiff, _e); if(onSwipeResult !== undefined && onSwipeResult) { this.reset(); } diff --git a/src/helpers/dom/handleTabSwipe.ts b/src/helpers/dom/handleTabSwipe.ts new file mode 100644 index 00000000..41be7dd5 --- /dev/null +++ b/src/helpers/dom/handleTabSwipe.ts @@ -0,0 +1,56 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import { cancelContextMenuOpening } from "../../components/misc"; +import SwipeHandler from "../../components/swipeHandler"; +import { cancelEvent } from "./cancelEvent"; + +export default function handleTabSwipe(container: HTMLElement, onSwipe: (next: boolean) => void) { + /* let hadScroll = false; + const onScroll = () => { + swipeHandler.reset(); + }; + let firstSwipeChecked = false; */ + return new SwipeHandler({ + element: container, + /* onFirstSwipe: () => { + this.scroll.container.addEventListener('scroll', onScroll, {passive: true}); + }, */ + onSwipe: (xDiff, yDiff, e) => { + /* if(!firstSwipeChecked) { + firstSwipeChecked = true; + if(yDiff !== 0) { + return true; + } + } + + cancelEvent(e); */ + + if(Math.abs(yDiff) > 20) { + return true; + } + + if(Math.abs(xDiff) > Math.abs(yDiff)) { + cancelEvent(e); + } else if(Math.abs(yDiff) > Math.abs(xDiff)/* || Math.abs(yDiff) > 20 */) { + return true; + } + + if(Math.abs(xDiff) > 50) { + onSwipe(xDiff > 0); + cancelContextMenuOpening(); + + return true; + } + }, + /* onReset: () => { + hadScroll = false; + firstSwipeChecked = false; + this.scroll.container.removeEventListener('scroll', onScroll); + }, */ + cancelEvent: false + }); +} \ No newline at end of file diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index b9acf4c6..b7d9126f 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -45,6 +45,8 @@ import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl" import { fastRafPromise } from "../../helpers/schedulers"; import appPhotosManager from "./appPhotosManager"; import SortedUserList from "../../components/sortedUserList"; +import { isTouchSupported } from "../../helpers/touchSupport"; +import handleTabSwipe from "../../helpers/dom/handleTabSwipe"; export type DialogDom = { avatarEl: AvatarElement, @@ -145,6 +147,13 @@ export class AppDialogsManager { }); } */ + if(isTouchSupported) { + handleTabSwipe(this.folders.container, (next) => { + const prevId = selectTab.prevId(); + selectTab(next ? prevId + 1 : prevId - 1); + }); + } + this.filterId = 0; this.addFilter({ id: this.filterId, diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 1ce57fac..da39bcc0 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -127,6 +127,8 @@ export type BroadcastEvents = { 'download_start': string, 'download_progress': any, + + 'context_menu_toggle': boolean }; export class RootScope extends EventListenerBase<{