Tab swipe
This commit is contained in:
parent
6a47b10ade
commit
76c5c066c6
@ -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');
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
56
src/helpers/dom/handleTabSwipe.ts
Normal file
56
src/helpers/dom/handleTabSwipe.ts
Normal file
@ -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
|
||||
});
|
||||
}
|
@ -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,
|
||||
|
@ -127,6 +127,8 @@ export type BroadcastEvents = {
|
||||
|
||||
'download_start': string,
|
||||
'download_progress': any,
|
||||
|
||||
'context_menu_toggle': boolean
|
||||
};
|
||||
|
||||
export class RootScope extends EventListenerBase<{
|
||||
|
Loading…
Reference in New Issue
Block a user