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 Scrollable, { ScrollableX } from "./scrollable";
|
||||||
import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers";
|
import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers";
|
||||||
import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
||||||
import { isSafari } from "../helpers/userAgent";
|
|
||||||
import { LangPackKey, i18n } from "../lib/langPack";
|
import { LangPackKey, i18n } from "../lib/langPack";
|
||||||
import findUpClassName from "../helpers/dom/findUpClassName";
|
import findUpClassName from "../helpers/dom/findUpClassName";
|
||||||
import { getMiddleware } from "../helpers/middleware";
|
import { getMiddleware } from "../helpers/middleware";
|
||||||
@ -39,6 +38,8 @@ import mediaSizes from "../helpers/mediaSizes";
|
|||||||
import appImManager from "../lib/appManagers/appImManager";
|
import appImManager from "../lib/appManagers/appImManager";
|
||||||
import positionElementByIndex from "../helpers/dom/positionElementByIndex";
|
import positionElementByIndex from "../helpers/dom/positionElementByIndex";
|
||||||
import cleanSearchText from "../helpers/cleanSearchText";
|
import cleanSearchText from "../helpers/cleanSearchText";
|
||||||
|
import { isTouchSupported } from "../helpers/touchSupport";
|
||||||
|
import handleTabSwipe from "../helpers/dom/handleTabSwipe";
|
||||||
|
|
||||||
//const testScroll = false;
|
//const testScroll = false;
|
||||||
|
|
||||||
@ -164,6 +165,13 @@ export default class AppSearchSuper {
|
|||||||
this.tabsContainer = document.createElement('div');
|
this.tabsContainer = document.createElement('div');
|
||||||
this.tabsContainer.classList.add('search-super-tabs-container', 'tabs-container');
|
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) {
|
for(const mediaTab of this.mediaTabs) {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
container.classList.add('search-super-container-' + mediaTab.type, 'tabs-tab');
|
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 mediaSizes from "../helpers/mediaSizes";
|
||||||
import { isTouchSupported } from "../helpers/touchSupport";
|
import { isTouchSupported } from "../helpers/touchSupport";
|
||||||
import { isApple, isMobileSafari } from "../helpers/userAgent";
|
import { isApple, isMobileSafari } from "../helpers/userAgent";
|
||||||
|
import rootScope from "../lib/rootScope";
|
||||||
import appNavigationController from "./appNavigationController";
|
import appNavigationController from "./appNavigationController";
|
||||||
|
|
||||||
export function putPreloader(elem: Element, returnDiv = false): HTMLElement {
|
export function putPreloader(elem: Element, returnDiv = false): HTMLElement {
|
||||||
@ -124,6 +125,8 @@ export const closeBtnMenu = () => {
|
|||||||
//openedMenu.previousElementSibling.remove(); // remove overlay
|
//openedMenu.previousElementSibling.remove(); // remove overlay
|
||||||
if(menuOverlay) menuOverlay.remove();
|
if(menuOverlay) menuOverlay.remove();
|
||||||
openedMenu = null;
|
openedMenu = null;
|
||||||
|
|
||||||
|
rootScope.dispatchEvent('context_menu_toggle', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(openedMenuOnClose) {
|
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
|
// ! safari iOS doesn't handle window click event on overlay, idk why
|
||||||
document.addEventListener(CLICK_EVENT_NAME, onClick);
|
document.addEventListener(CLICK_EVENT_NAME, onClick);
|
||||||
|
|
||||||
|
rootScope.dispatchEvent('context_menu_toggle', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PADDING_TOP = 8;
|
const PADDING_TOP = 8;
|
||||||
@ -307,6 +312,20 @@ export function positionMenu({pageX, pageY}: MouseEvent | Touch, elem: HTMLEleme
|
|||||||
(side === 'center' ? side : (side === 'left' ? 'right' : 'left')));
|
(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) {
|
export function attachContextMenuListener(element: HTMLElement, callback: (e: Touch | MouseEvent) => void, listenerSetter?: ListenerSetter) {
|
||||||
const add = listenerSetter ? listenerSetter.add(element) : element.addEventListener.bind(element);
|
const add = listenerSetter ? listenerSetter.add(element) : element.addEventListener.bind(element);
|
||||||
const remove = listenerSetter ? listenerSetter.removeManual.bind(listenerSetter, element) : element.removeEventListener.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);
|
add('touchcancel', onCancel, options);
|
||||||
|
|
||||||
timeout = window.setTimeout(() => {
|
timeout = window.setTimeout(() => {
|
||||||
|
if(_cancelContextMenuOpening) {
|
||||||
|
onCancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
callback(e.touches[0]);
|
callback(e.touches[0]);
|
||||||
onCancel();
|
onCancel();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||||
import { safeAssign } from "../helpers/object";
|
import { safeAssign } from "../helpers/object";
|
||||||
import { isTouchSupported } from "../helpers/touchSupport";
|
import { isTouchSupported } from "../helpers/touchSupport";
|
||||||
|
import rootScope from "../lib/rootScope";
|
||||||
|
|
||||||
const getEvent = (e: TouchEvent | MouseEvent) => {
|
const getEvent = (e: TouchEvent | MouseEvent) => {
|
||||||
return (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as 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;
|
const attachGlobalListenerTo = window;
|
||||||
|
|
||||||
|
let RESET_GLOBAL = false;
|
||||||
|
rootScope.addEventListener('context_menu_toggle', (visible) => {
|
||||||
|
RESET_GLOBAL = visible;
|
||||||
|
});
|
||||||
|
|
||||||
export default class SwipeHandler {
|
export default class SwipeHandler {
|
||||||
private element: HTMLElement;
|
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 verifyTouchTarget: (evt: TouchEvent | MouseEvent) => boolean;
|
||||||
private onFirstSwipe: () => void;
|
private onFirstSwipe: () => void;
|
||||||
private onReset: () => void;
|
private onReset: () => void;
|
||||||
private cursor: 'grabbing' | 'move' = 'grabbing';
|
private cursor: 'grabbing' | 'move' = 'grabbing';
|
||||||
|
private cancelEvent = true;
|
||||||
|
|
||||||
private hadMove = false;
|
private hadMove = false;
|
||||||
private xDown: number = null;
|
private xDown: number = null;
|
||||||
@ -32,7 +39,8 @@ export default class SwipeHandler {
|
|||||||
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
|
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
|
||||||
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
|
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
|
||||||
onReset?: SwipeHandler['onReset'],
|
onReset?: SwipeHandler['onReset'],
|
||||||
cursor?: SwipeHandler['cursor']
|
cursor?: SwipeHandler['cursor'],
|
||||||
|
cancelEvent?: SwipeHandler['cancelEvent']
|
||||||
}) {
|
}) {
|
||||||
safeAssign(this, options);
|
safeAssign(this, options);
|
||||||
|
|
||||||
@ -96,11 +104,14 @@ export default class SwipeHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleMove = (_e: TouchEvent | MouseEvent) => {
|
handleMove = (_e: TouchEvent | MouseEvent) => {
|
||||||
if(this.xDown === null || this.yDown === null) {
|
if(this.xDown === null || this.yDown === null || RESET_GLOBAL) {
|
||||||
return this.reset();
|
this.reset();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelEvent(_e);
|
if(this.cancelEvent) {
|
||||||
|
cancelEvent(_e);
|
||||||
|
}
|
||||||
|
|
||||||
const e = getEvent(_e);
|
const e = getEvent(_e);
|
||||||
const xUp = e.clientX;
|
const xUp = e.clientX;
|
||||||
@ -140,7 +151,7 @@ export default class SwipeHandler {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
/* reset values */
|
/* reset values */
|
||||||
const onSwipeResult = this.onSwipe(xDiff, yDiff);
|
const onSwipeResult = this.onSwipe(xDiff, yDiff, _e);
|
||||||
if(onSwipeResult !== undefined && onSwipeResult) {
|
if(onSwipeResult !== undefined && onSwipeResult) {
|
||||||
this.reset();
|
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 { fastRafPromise } from "../../helpers/schedulers";
|
||||||
import appPhotosManager from "./appPhotosManager";
|
import appPhotosManager from "./appPhotosManager";
|
||||||
import SortedUserList from "../../components/sortedUserList";
|
import SortedUserList from "../../components/sortedUserList";
|
||||||
|
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||||
|
import handleTabSwipe from "../../helpers/dom/handleTabSwipe";
|
||||||
|
|
||||||
export type DialogDom = {
|
export type DialogDom = {
|
||||||
avatarEl: AvatarElement,
|
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.filterId = 0;
|
||||||
this.addFilter({
|
this.addFilter({
|
||||||
id: this.filterId,
|
id: this.filterId,
|
||||||
|
@ -127,6 +127,8 @@ export type BroadcastEvents = {
|
|||||||
|
|
||||||
'download_start': string,
|
'download_start': string,
|
||||||
'download_progress': any,
|
'download_progress': any,
|
||||||
|
|
||||||
|
'context_menu_toggle': boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export class RootScope extends EventListenerBase<{
|
export class RootScope extends EventListenerBase<{
|
||||||
|
Loading…
Reference in New Issue
Block a user