Browse Source

Tab swipe

master
Eduard Kuzmenko 3 years ago
parent
commit
76c5c066c6
  1. 10
      src/components/appSearchSuper..ts
  2. 24
      src/components/misc.ts
  3. 23
      src/components/swipeHandler.ts
  4. 56
      src/helpers/dom/handleTabSwipe.ts
  5. 9
      src/lib/appManagers/appDialogsManager.ts
  6. 2
      src/lib/rootScope.ts

10
src/components/appSearchSuper..ts

@ -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');

24
src/components/misc.ts

@ -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();

23
src/components/swipeHandler.ts

@ -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

@ -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
});
}

9
src/lib/appManagers/appDialogsManager.ts

@ -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,

2
src/lib/rootScope.ts

@ -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…
Cancel
Save