Touch fixes

This commit is contained in:
Eduard Kuzmenko 2022-02-11 18:36:00 +04:00
parent e74ac65ca4
commit 72479fc179
15 changed files with 91 additions and 37 deletions

View File

@ -6,7 +6,7 @@
import MEDIA_MIME_TYPES_SUPPORTED from "../environment/mediaMimeTypesSupport"; import MEDIA_MIME_TYPES_SUPPORTED from "../environment/mediaMimeTypesSupport";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
import { attachClickEvent } from "../helpers/dom/clickEvent"; import { attachClickEvent, detachClickEvent } from "../helpers/dom/clickEvent";
import setInnerHTML from "../helpers/dom/setInnerHTML"; import setInnerHTML from "../helpers/dom/setInnerHTML";
import mediaSizes from "../helpers/mediaSizes"; import mediaSizes from "../helpers/mediaSizes";
import SearchListLoader from "../helpers/searchListLoader"; import SearchListLoader from "../helpers/searchListLoader";
@ -123,8 +123,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
protected setListeners() { protected setListeners() {
super.setListeners(); super.setListeners();
this.buttons.forward.addEventListener('click', this.onForwardClick); attachClickEvent(this.buttons.forward, this.onForwardClick);
this.author.container.addEventListener('click', this.onAuthorClick); attachClickEvent(this.author.container, this.onAuthorClick);
const onCaptionClick = (e: MouseEvent) => { const onCaptionClick = (e: MouseEvent) => {
if(e.target instanceof HTMLAnchorElement) { // close viewer if it's t.me/ redirect if(e.target instanceof HTMLAnchorElement) { // close viewer if it's t.me/ redirect
@ -136,14 +136,15 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
cancelEvent(e); cancelEvent(e);
this.close().then(() => { this.close().then(() => {
this.content.caption.removeEventListener('click', onCaptionClick, {capture: true}); detachClickEvent(this.content.caption, onCaptionClick, {capture: true});
(e.target as HTMLAnchorElement).click(); (e.target as HTMLAnchorElement).click();
}); });
return false; return false;
} }
}; };
this.content.caption.addEventListener('click', onCaptionClick, {capture: true});
attachClickEvent(this.content.caption, onCaptionClick, {capture: true});
} }
/* public close(e?: MouseEvent) { /* public close(e?: MouseEvent) {

View File

@ -43,6 +43,7 @@ import { MyMessage } from "../lib/appManagers/appMessagesManager";
import RichTextProcessor from "../lib/richtextprocessor"; import RichTextProcessor from "../lib/richtextprocessor";
import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config"; import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config";
import { isFullScreen } from "../helpers/dom/fullScreen"; import { isFullScreen } from "../helpers/dom/fullScreen";
import { attachClickEvent } from "../helpers/dom/clickEvent";
const ZOOM_STEP = 0.5; const ZOOM_STEP = 0.5;
const ZOOM_INITIAL_VALUE = 1; const ZOOM_INITIAL_VALUE = 1;
@ -190,9 +191,9 @@ export default class AppMediaViewerBase<
this.zoomElements.container.classList.add('zoom-container'); this.zoomElements.container.classList.add('zoom-container');
this.zoomElements.btnOut = ButtonIcon('zoomout', {noRipple: true}); this.zoomElements.btnOut = ButtonIcon('zoomout', {noRipple: true});
this.zoomElements.btnOut.addEventListener('click', () => this.changeZoom(false)); attachClickEvent(this.zoomElements.btnOut, () => this.changeZoom(false));
this.zoomElements.btnIn = ButtonIcon('zoomin', {noRipple: true}); this.zoomElements.btnIn = ButtonIcon('zoomin', {noRipple: true});
this.zoomElements.btnIn.addEventListener('click', () => this.changeZoom(true)); attachClickEvent(this.zoomElements.btnIn, () => this.changeZoom(true));
this.zoomElements.rangeSelector = new RangeSelector({ this.zoomElements.rangeSelector = new RangeSelector({
step: ZOOM_STEP, step: ZOOM_STEP,
@ -254,13 +255,13 @@ export default class AppMediaViewerBase<
} }
protected setListeners() { protected setListeners() {
this.buttons.download.addEventListener('click', this.onDownloadClick); attachClickEvent(this.buttons.download, this.onDownloadClick);
[this.buttons.close, this.buttons['mobile-close'], this.preloaderStreamable.preloader].forEach(el => { [this.buttons.close, this.buttons['mobile-close'], this.preloaderStreamable.preloader].forEach(el => {
el.addEventListener('click', this.close.bind(this)); attachClickEvent(el, this.close.bind(this));
}); });
([[-1, this.buttons.prev], [1, this.buttons.next]] as [number, HTMLElement][]).forEach(([moveLength, button]) => { ([[-1, this.buttons.prev], [1, this.buttons.next]] as [number, HTMLElement][]).forEach(([moveLength, button]) => {
button.addEventListener('click', (e) => { attachClickEvent(button, (e) => {
cancelEvent(e); cancelEvent(e);
if(this.setMoverPromise) return; if(this.setMoverPromise) return;
@ -268,14 +269,14 @@ export default class AppMediaViewerBase<
}); });
}); });
this.buttons.zoom.addEventListener('click', () => { attachClickEvent(this.buttons.zoom, () => {
if(this.isZooming()) this.toggleZoom(false); if(this.isZooming()) this.toggleZoom(false);
else { else {
this.changeZoom(true); this.changeZoom(true);
} }
}); });
this.wholeDiv.addEventListener('click', this.onClick); attachClickEvent(this.wholeDiv, this.onClick);
this.listLoader.onJump = (item, older) => { this.listLoader.onJump = (item, older) => {
if(older) this.onNextClick(item); if(older) this.onNextClick(item);
@ -1359,7 +1360,14 @@ export default class AppMediaViewerBase<
// const play = useController ? appMediaPlaybackController.willBePlayedMedia === video : true; // const play = useController ? appMediaPlaybackController.willBePlayedMedia === video : true;
const play = true; const play = true;
const player = this.videoPlayer = new VideoPlayer(video, play, supportsStreaming); const player = this.videoPlayer = new VideoPlayer({
video,
play,
streamable: supportsStreaming,
onPlaybackRackMenuToggle: (open) => {
this.wholeDiv.classList.toggle('hide-caption', !!open);
}
});
player.addEventListener('toggleControls', (show) => { player.addEventListener('toggleControls', (show) => {
this.wholeDiv.classList.toggle('has-video-controls', show); this.wholeDiv.classList.toggle('has-video-controls', show);
}); });

View File

@ -1100,7 +1100,7 @@ export default class AppSearchSuper {
}); });
showMore.append(intlElement.element); showMore.append(intlElement.element);
this.searchGroups.globalContacts.nameEl.append(showMore); this.searchGroups.globalContacts.nameEl.append(showMore);
showMore.addEventListener('click', () => { attachClickEvent(showMore, () => {
const isShort = this.searchGroups.globalContacts.container.classList.toggle('is-short'); const isShort = this.searchGroups.globalContacts.container.classList.toggle('is-short');
intlElement.key = isShort ? 'Separator.ShowMore' : 'Separator.ShowLess'; intlElement.key = isShort ? 'Separator.ShowMore' : 'Separator.ShowLess';
intlElement.update(); intlElement.update();
@ -1194,7 +1194,7 @@ export default class AppSearchSuper {
if(!this.membersList) { if(!this.membersList) {
this.membersList = new SortedUserList({lazyLoadQueue: this.lazyLoadQueue, rippleEnabled: false}); this.membersList = new SortedUserList({lazyLoadQueue: this.lazyLoadQueue, rippleEnabled: false});
this.membersList.list.addEventListener('click', (e) => { attachClickEvent(this.membersList.list, (e) => {
const li = findUpTag(e.target, 'LI'); const li = findUpTag(e.target, 'LI');
if(!li) { if(!li) {
return; return;

View File

@ -25,6 +25,7 @@ import debounce from "../helpers/schedulers/debounce";
import windowSize from "../helpers/windowSize"; import windowSize from "../helpers/windowSize";
import appPeersManager, { IsPeerType } from "../lib/appManagers/appPeersManager"; import appPeersManager, { IsPeerType } from "../lib/appManagers/appPeersManager";
import { generateDelimiter, SettingSection } from "./sidebarLeft"; import { generateDelimiter, SettingSection } from "./sidebarLeft";
import { attachClickEvent } from "../helpers/dom/clickEvent";
type SelectSearchPeerType = 'contacts' | 'dialogs' | 'channelParticipants'; type SelectSearchPeerType = 'contacts' | 'dialogs' | 'channelParticipants';
@ -157,7 +158,7 @@ export default class AppSelectPeers {
// let delimiter = document.createElement('hr'); // let delimiter = document.createElement('hr');
this.selectedContainer.addEventListener('click', (e) => { attachClickEvent(this.selectedContainer, (e) => {
if(this.freezed) return; if(this.freezed) return;
let target = e.target as HTMLElement; let target = e.target as HTMLElement;
target = findUpClassName(target, 'selector-user'); target = findUpClassName(target, 'selector-user');
@ -188,7 +189,7 @@ export default class AppSelectPeers {
this.scrollable = new Scrollable(this.chatsContainer); this.scrollable = new Scrollable(this.chatsContainer);
this.scrollable.setVirtualContainer(this.list); this.scrollable.setVirtualContainer(this.list);
this.chatsContainer.addEventListener('click', (e) => { attachClickEvent(this.chatsContainer, (e) => {
const target = findUpAttribute(e.target, 'data-peer-id') as HTMLElement; const target = findUpAttribute(e.target, 'data-peer-id') as HTMLElement;
cancelEvent(e); cancelEvent(e);

View File

@ -5,6 +5,7 @@
*/ */
import type { CancellablePromise } from "../helpers/cancellablePromise"; import type { CancellablePromise } from "../helpers/cancellablePromise";
import { attachClickEvent } from "../helpers/dom/clickEvent";
import type { InputFile } from "../layer"; import type { InputFile } from "../layer";
import PopupAvatar from "./popups/avatar"; import PopupAvatar from "./popups/avatar";
@ -25,7 +26,7 @@ export default class AvatarEdit {
this.container.append(this.canvas, this.icon); this.container.append(this.canvas, this.icon);
this.container.addEventListener('click', () => { attachClickEvent(this.container, () => {
new PopupAvatar().open(this.canvas, onChange); new PopupAvatar().open(this.canvas, onChange);
}); });
} }

View File

@ -23,7 +23,7 @@ const ButtonMenuToggle = (options: Partial<{noRipple: true, onlyMobile: true, li
}; };
// TODO: refactor for attachClickEvent, because if move finger after touchstart, it will start anyway // TODO: refactor for attachClickEvent, because if move finger after touchstart, it will start anyway
const ButtonMenuToggleHandler = (el: HTMLElement, onOpen?: (e: Event) => void, options?: AttachClickOptions) => { const ButtonMenuToggleHandler = (el: HTMLElement, onOpen?: (e: Event) => void, options?: AttachClickOptions, onClose?: () => void) => {
const add = options?.listenerSetter ? options.listenerSetter.add(el) : el.addEventListener.bind(el); const add = options?.listenerSetter ? options.listenerSetter.add(el) : el.addEventListener.bind(el);
//console.trace('ButtonMenuToggleHandler attach', el, onOpen, options); //console.trace('ButtonMenuToggleHandler attach', el, onOpen, options);
@ -39,7 +39,7 @@ const ButtonMenuToggleHandler = (el: HTMLElement, onOpen?: (e: Event) => void, o
closeBtnMenu(); closeBtnMenu();
} else { } else {
onOpen && onOpen(e); onOpen && onOpen(e);
openBtnMenu(openedMenu); openBtnMenu(openedMenu, onClose);
} }
}); });
}; };

View File

@ -522,9 +522,6 @@ export default class ChatBubbles {
}); });
}); });
// attachClickEvent(this.bubblesContainer, this.onBubblesClick, {listenerSetter: this.listenerSetter});
this.listenerSetter.add(this.bubblesContainer)('click', this.onBubblesClick/* , {capture: true, passive: false} */);
if(IS_TOUCH_SUPPORTED) { if(IS_TOUCH_SUPPORTED) {
const className = 'is-gesturing-reply'; const className = 'is-gesturing-reply';
const MAX = 64; const MAX = 64;
@ -593,6 +590,9 @@ export default class ChatBubbles {
}); });
} }
attachClickEvent(this.bubblesContainer, this.onBubblesClick, {listenerSetter: this.listenerSetter});
// this.listenerSetter.add(this.bubblesContainer)('click', this.onBubblesClick/* , {capture: true, passive: false} */);
if(DEBUG) { if(DEBUG) {
this.listenerSetter.add(this.bubblesContainer)('dblclick', (e) => { this.listenerSetter.add(this.bubblesContainer)('dblclick', (e) => {
const bubble = findUpClassName(e.target, 'grouped-item') || findUpClassName(e.target, 'bubble'); const bubble = findUpClassName(e.target, 'grouped-item') || findUpClassName(e.target, 'bubble');

View File

@ -42,6 +42,7 @@ import AppPrivateSearchTab from "../sidebarRight/tabs/search";
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
import mediaSizes from "../../helpers/mediaSizes"; import mediaSizes from "../../helpers/mediaSizes";
import ChatSearch from "./search"; import ChatSearch from "./search";
import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport";
export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled'; export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled';
@ -216,7 +217,7 @@ export default class Chat extends EventListenerBase<{
this.input.constructPeerHelpers(); this.input.constructPeerHelpers();
} }
if(this.type !== 'scheduled') { if(this.type !== 'scheduled' && !IS_TOUCH_SUPPORTED) {
this.bubbles.setReactionsHoverListeners(); this.bubbles.setReactionsHoverListeners();
} }

View File

@ -18,6 +18,7 @@ import sessionStorage from '../lib/sessionStorage';
import { ConnectionStatus } from "../lib/mtproto/connectionStatus"; import { ConnectionStatus } from "../lib/mtproto/connectionStatus";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
import apiManager from "../lib/mtproto/mtprotoworker"; import apiManager from "../lib/mtproto/mtprotoworker";
import { attachClickEvent } from "../helpers/dom/clickEvent";
export default class ConnectionStatusComponent { export default class ConnectionStatusComponent {
public static CHANGE_STATE_DELAY = 1000; public static CHANGE_STATE_DELAY = 1000;
@ -133,7 +134,7 @@ export default class ConnectionStatusComponent {
const a = document.createElement('a'); const a = document.createElement('a');
a.classList.add('force-reconnect'); a.classList.add('force-reconnect');
a.append(i18n(langPackKey)); a.append(i18n(langPackKey));
a.addEventListener('click', (e) => { attachClickEvent(a, (e) => {
cancelEvent(e); cancelEvent(e);
callback(); callback();
}); });

View File

@ -45,7 +45,7 @@ export function attachClickEvent(elem: HTMLElement | Window, callback: (e: /* To
add(CLICK_EVENT_NAME, callback, options); add(CLICK_EVENT_NAME, callback, options);
} }
export function detachClickEvent(elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options?: AddEventListenerOptions) { export function detachClickEvent(elem: HTMLElement, callback: (e: /* TouchEvent | */MouseEvent) => void, options?: AddEventListenerOptions) {
// if(CLICK_EVENT_NAME === 'touchend') { // if(CLICK_EVENT_NAME === 'touchend') {
// elem.removeEventListener('touchstart', callback, options); // elem.removeEventListener('touchstart', callback, options);
// } else { // } else {

View File

@ -20,6 +20,7 @@ export default class ControlsHover extends EventListenerBase<{
protected element: HTMLElement; protected element: HTMLElement;
protected listenerSetter: ListenerSetter; protected listenerSetter: ListenerSetter;
protected showOnLeaveToClassName: string; protected showOnLeaveToClassName: string;
protected ignoreClickClassName: string;
constructor() { constructor() {
super(false); super(false);
@ -30,14 +31,19 @@ export default class ControlsHover extends EventListenerBase<{
element: HTMLElement, element: HTMLElement,
listenerSetter: ListenerSetter, listenerSetter: ListenerSetter,
canHideControls?: () => boolean, canHideControls?: () => boolean,
showOnLeaveToClassName?: string showOnLeaveToClassName?: string,
ignoreClickClassName?: string
}) { }) {
safeAssign(this, options); safeAssign(this, options);
const {listenerSetter, element} = this; const {listenerSetter, element} = this;
if(IS_TOUCH_SUPPORTED) { if(IS_TOUCH_SUPPORTED) {
listenerSetter.add(element)('click', () => { listenerSetter.add(element)('click', (e) => {
if(this.ignoreClickClassName && findUpClassName(e.target, this.ignoreClickClassName)) {
return;
}
this.toggleControls(); this.toggleControls();
}); });

View File

@ -266,6 +266,7 @@ export default class VideoPlayer extends ControlsHover {
private static PLAYBACK_RATES = [0.5, 1, 1.5, 2]; private static PLAYBACK_RATES = [0.5, 1, 1.5, 2];
private static PLAYBACK_RATES_ICONS = ['playback_05', 'playback_1x', 'playback_15', 'playback_2x']; private static PLAYBACK_RATES_ICONS = ['playback_05', 'playback_1x', 'playback_15', 'playback_2x'];
protected video: HTMLVideoElement;
protected wrapper: HTMLDivElement; protected wrapper: HTMLDivElement;
protected progress: MediaProgressLine; protected progress: MediaProgressLine;
protected skin: 'default'; protected skin: 'default';
@ -276,12 +277,23 @@ export default class VideoPlayer extends ControlsHover {
/* protected videoParent: HTMLElement; /* protected videoParent: HTMLElement;
protected videoWhichChild: number; */ protected videoWhichChild: number; */
constructor(protected video: HTMLVideoElement, play = false, streamable = false, duration?: number) { protected onPlaybackRackMenuToggle?: (open: boolean) => void;
constructor({video, play = false, streamable = false, duration, onPlaybackRackMenuToggle}: {
video: HTMLVideoElement,
play?: boolean,
streamable?: boolean,
duration?: number,
onPlaybackRackMenuToggle?: (open: boolean) => void
}) {
super(); super();
this.video = video;
this.wrapper = document.createElement('div'); this.wrapper = document.createElement('div');
this.wrapper.classList.add('ckin__player'); this.wrapper.classList.add('ckin__player');
this.onPlaybackRackMenuToggle = onPlaybackRackMenuToggle;
this.listenerSetter = new ListenerSetter(); this.listenerSetter = new ListenerSetter();
this.setup({ this.setup({
@ -290,7 +302,8 @@ export default class VideoPlayer extends ControlsHover {
canHideControls: () => { canHideControls: () => {
return !this.video.paused && (!this.playbackRateButton || !this.playbackRateButton.classList.contains('menu-open')); return !this.video.paused && (!this.playbackRateButton || !this.playbackRateButton.classList.contains('menu-open'));
}, },
showOnLeaveToClassName: 'media-viewer-caption' showOnLeaveToClassName: 'media-viewer-caption',
ignoreClickClassName: 'ckin__controls'
}); });
video.parentNode.insertBefore(this.wrapper, video); video.parentNode.insertBefore(this.wrapper, video);
@ -423,9 +436,11 @@ export default class VideoPlayer extends ControlsHover {
listenerSetter.add(video)('play', () => { listenerSetter.add(video)('play', () => {
wrapper.classList.add('played'); wrapper.classList.add('played');
if(!IS_TOUCH_SUPPORTED) {
listenerSetter.add(video)('play', () => { listenerSetter.add(video)('play', () => {
this.hideControls(true); this.hideControls(true);
}); });
}
}, {once: true}); }, {once: true});
listenerSetter.add(video)('pause', () => { listenerSetter.add(video)('pause', () => {
@ -495,7 +510,16 @@ export default class VideoPlayer extends ControlsHover {
}); });
const btnMenu = ButtonMenu(buttons); const btnMenu = ButtonMenu(buttons);
btnMenu.classList.add('top-left'); btnMenu.classList.add('top-left');
ButtonMenuToggleHandler(this.playbackRateButton); ButtonMenuToggleHandler(
this.playbackRateButton,
this.onPlaybackRackMenuToggle ? () => {
this.onPlaybackRackMenuToggle(true);
} : undefined,
undefined,
this.onPlaybackRackMenuToggle ? () => {
this.onPlaybackRackMenuToggle(false);
} : undefined
);
this.playbackRateButton.append(btnMenu); this.playbackRateButton.append(btnMenu);
this.setPlaybackRateIcon(); this.setPlaybackRateIcon();
@ -572,5 +596,6 @@ export default class VideoPlayer extends ControlsHover {
super.cleanup(); super.cleanup();
this.listenerSetter.removeAll(); this.listenerSetter.removeAll();
this.progress.removeListeners(); this.progress.removeListeners();
this.onPlaybackRackMenuToggle = undefined;
} }
} }

View File

@ -92,7 +92,10 @@
left: 50%; left: 50%;
transform: translate3d(-50%, -50%, 0) scale(1); transform: translate3d(-50%, -50%, 0) scale(1);
font-size: 4rem; font-size: 4rem;
@include respond-to(not-handhelds) {
pointer-events: none; pointer-events: none;
}
@include animation-level(2) { @include animation-level(2) {
transition: visibility var(--layer-transition), opacity var(--layer-transition); transition: visibility var(--layer-transition), opacity var(--layer-transition);

View File

@ -533,7 +533,7 @@ $inactive-opacity: .4;
transition: opacity var(--open-duration) 0s, visibility 0s var(--open-duration); transition: opacity var(--open-duration) 0s, visibility 0s var(--open-duration);
} */ } */
.btn-menu-toggle { .btn-menu-toggle:not(.playback-rate) {
color: rgba(255, 255, 255, $inactive-opacity); color: rgba(255, 255, 255, $inactive-opacity);
opacity: 1; opacity: 1;
@ -542,6 +542,13 @@ $inactive-opacity: .4;
background-color: rgba(112, 117, 121, .2) !important; background-color: rgba(112, 117, 121, .2) !important;
} }
} }
&.hide-caption {
.media-viewer-caption {
opacity: 0 !important;
pointer-events: none;
}
}
} }
&.highlight-switchers { &.highlight-switchers {

View File

@ -44,7 +44,7 @@
&.has-animation { &.has-animation {
> .media-sticker { > .media-sticker {
visibility: hidden; opacity: 0;
} }
} }