Fix context menu again
Multiselect fixes for desktop & mobile Safari Fix multiselect album on mobiles
This commit is contained in:
parent
50915cf493
commit
984b04ab40
@ -1173,7 +1173,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
this.openMedia(appMessagesManager.getMessage(target.mid), target.element);
|
||||
};
|
||||
|
||||
onForwardClick = (e: MouseEvent) => {
|
||||
onForwardClick = () => {
|
||||
if(this.currentMessageID) {
|
||||
//appSidebarRight.forwardTab.open([this.currentMessageID]);
|
||||
new PopupForward([this.currentMessageID], () => {
|
||||
@ -1198,7 +1198,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||
}
|
||||
};
|
||||
|
||||
onDownloadClick = (e: MouseEvent) => {
|
||||
onDownloadClick = () => {
|
||||
const message = appMessagesManager.getMessage(this.currentMessageID);
|
||||
if(message.media.photo) {
|
||||
appPhotosManager.savePhotoFile(message.media.photo);
|
||||
@ -1370,7 +1370,7 @@ export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMe
|
||||
this.openMedia(target.photoID, target.element, 1);
|
||||
};
|
||||
|
||||
onDownloadClick = (e: MouseEvent) => {
|
||||
onDownloadClick = () => {
|
||||
appPhotosManager.savePhotoFile(appPhotosManager.getPhoto(this.currentPhotoID));
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,7 @@ import { isSafari } from "../helpers/userAgent";
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import './middleEllipsis';
|
||||
import { cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom";
|
||||
|
||||
rootScope.on('messages_media_read', e => {
|
||||
const mids = e.detail;
|
||||
@ -213,12 +214,14 @@ function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement, mid: number) {
|
||||
mousedown = false;
|
||||
}
|
||||
});
|
||||
progress.addEventListener('click', (e) => {
|
||||
progress.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
if(!audio.paused) scrub(e);
|
||||
});
|
||||
|
||||
function scrub(e: MouseEvent) {
|
||||
const scrubTime = e.offsetX / availW /* width */ * audio.duration;
|
||||
function scrub(e: MouseEvent | TouchEvent) {
|
||||
const offsetX = e instanceof MouseEvent ? e.offsetX : e.changedTouches[0].clientX;
|
||||
const scrubTime = offsetX / availW /* width */ * audio.duration;
|
||||
lastIndex = Math.round(scrubTime / audio.duration * barCount);
|
||||
|
||||
rects.slice(0, lastIndex + 1).forEach(node => node.classList.add('active'));
|
||||
@ -366,7 +369,8 @@ export default class AudioElement extends HTMLElement {
|
||||
audioTimeDiv.innerText = String(audio.currentTime | 0).toHHMMSS(true) + ' / ' + durationStr;
|
||||
}
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
toggle.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
if(audio.paused) audio.play().catch(() => {});
|
||||
else audio.pause();
|
||||
});
|
||||
@ -395,7 +399,8 @@ export default class AudioElement extends HTMLElement {
|
||||
if(doc.type == 'voice') {
|
||||
let download: Download;
|
||||
|
||||
const onClick = () => {
|
||||
const onClick = (e: Event) => {
|
||||
cancelEvent(e);
|
||||
if(!download) {
|
||||
if(!preloader) {
|
||||
preloader = new ProgressivePreloader(null, true);
|
||||
@ -406,7 +411,7 @@ export default class AudioElement extends HTMLElement {
|
||||
|
||||
download.then(() => {
|
||||
downloadDiv.remove();
|
||||
this.removeEventListener('click', onClick);
|
||||
this.removeEventListener(CLICK_EVENT_NAME, onClick);
|
||||
onLoad();
|
||||
}).catch(err => {
|
||||
if(err.name === 'AbortError') {
|
||||
@ -422,7 +427,7 @@ export default class AudioElement extends HTMLElement {
|
||||
}
|
||||
};
|
||||
|
||||
this.addEventListener('click', onClick);
|
||||
this.addEventListener(CLICK_EVENT_NAME, onClick);
|
||||
this.click();
|
||||
} else {
|
||||
onLoad(false);
|
||||
@ -430,8 +435,9 @@ export default class AudioElement extends HTMLElement {
|
||||
//if(appMediaPlaybackController.mediaExists(mid)) { // чтобы показать прогресс, если аудио уже было скачано
|
||||
//onLoad();
|
||||
//} else {
|
||||
const r = () => {
|
||||
const r = (e: Event) => {
|
||||
//onLoad();
|
||||
cancelEvent(e);
|
||||
appMediaPlaybackController.resolveWaitingForLoadMedia(mid);
|
||||
|
||||
appMediaPlaybackController.willBePlayed(this.audio); // prepare for loading audio
|
||||
@ -464,7 +470,7 @@ export default class AudioElement extends HTMLElement {
|
||||
});
|
||||
};
|
||||
|
||||
this.addEventListener('click', r, {once: true});
|
||||
this.addEventListener(CLICK_EVENT_NAME, r, {once: true});
|
||||
//}
|
||||
}
|
||||
} else {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { CLICK_EVENT_NAME } from "../helpers/dom";
|
||||
import { ripple } from "./ripple";
|
||||
|
||||
export type ButtonMenuItemOptions = {icon: string, text: string, onClick: (e: MouseEvent) => void, element?: HTMLElement};
|
||||
export type ButtonMenuItemOptions = {icon: string, text: string, onClick: (e: MouseEvent | TouchEvent) => void, element?: HTMLElement};
|
||||
|
||||
const ButtonMenuItem = (options: ButtonMenuItemOptions) => {
|
||||
if(options.element) return options.element;
|
||||
@ -11,7 +12,7 @@ const ButtonMenuItem = (options: ButtonMenuItemOptions) => {
|
||||
el.innerText = text;
|
||||
|
||||
ripple(el);
|
||||
el.addEventListener('click', onClick);
|
||||
el.addEventListener(CLICK_EVENT_NAME, onClick);
|
||||
|
||||
return options.element = el;
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom";
|
||||
import ButtonIcon from "./buttonIcon";
|
||||
import ButtonMenu, { ButtonMenuItemOptions } from "./buttonMenu";
|
||||
import { closeBtnMenu, openBtnMenu } from "./misc";
|
||||
@ -12,14 +13,13 @@ const ButtonMenuToggle = (options: Partial<{noRipple: true, onlyMobile: true}> =
|
||||
};
|
||||
|
||||
const ButtonMenuToggleHandler = (el: HTMLElement) => {
|
||||
(el as HTMLElement).addEventListener('click', (e) => {
|
||||
(el as HTMLElement).addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
//console.log('click pageIm');
|
||||
if(!el.classList.contains('btn-menu-toggle')) return false;
|
||||
|
||||
//window.removeEventListener('mousemove', onMouseMove);
|
||||
const openedMenu = el.querySelector('.btn-menu') as HTMLDivElement;
|
||||
e.cancelBubble = true;
|
||||
//cancelEvent(e);
|
||||
cancelEvent(e);
|
||||
|
||||
if(el.classList.contains('menu-open')) {
|
||||
closeBtnMenu();
|
||||
|
@ -87,33 +87,60 @@ export default class ChatContextMenu {
|
||||
|
||||
const side: 'left' | 'right' = bubble.classList.contains('is-in') ? 'left' : 'right';
|
||||
//bubble.parentElement.append(this.element);
|
||||
//appImManager.log('contextmenu', e, bubble, side);
|
||||
positionMenu(e, this.element, side);
|
||||
openBtnMenu(this.element, () => {
|
||||
this.peerID = this.msgID = 0;
|
||||
this.target = null;
|
||||
});
|
||||
|
||||
/////this.log('contextmenu', e, bubble, msgID, side);
|
||||
};
|
||||
|
||||
if(isTouchSupported) {
|
||||
attachTo.addEventListener('click', (e) => {
|
||||
//const good = !!findUpClassName(e.target, 'message') || !!findUpClassName(e.target, 'bubble__container');
|
||||
const attachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent) => void) => {
|
||||
elem.addEventListener('touchstart', (e) => {
|
||||
const onTouchMove = (e: TouchEvent) => {
|
||||
elem.removeEventListener('touchend', onTouchEnd);
|
||||
};
|
||||
|
||||
const onTouchEnd = (e: TouchEvent) => {
|
||||
elem.removeEventListener('touchmove', onTouchMove);
|
||||
callback(e);
|
||||
};
|
||||
|
||||
elem.addEventListener('touchend', onTouchEnd, {once: true});
|
||||
elem.addEventListener('touchmove', onTouchMove, {once: true});
|
||||
});
|
||||
};
|
||||
|
||||
attachClickEvent(attachTo, (e) => {
|
||||
if(appImManager.chatSelection.isSelecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
const className = (e.target as HTMLElement).className;
|
||||
if(!className || !className.includes) return;
|
||||
|
||||
appImManager.log('touchend', e);
|
||||
|
||||
const good = ['bubble', 'bubble__container', 'message', 'time', 'inner'].find(c => className.match(new RegExp(c + '($|\\s)')));
|
||||
if(good) {
|
||||
onContextMenu(e);
|
||||
cancelEvent(e);
|
||||
onContextMenu(e.changedTouches[0]);
|
||||
}
|
||||
});
|
||||
|
||||
attachContextMenuListener(attachTo, (e) => {
|
||||
if(appImManager.chatSelection.isSelecting) return;
|
||||
|
||||
// * these two lines will fix instant text selection on iOS Safari
|
||||
attachTo.classList.add('no-select');
|
||||
attachTo.addEventListener('touchend', () => {
|
||||
attachTo.classList.remove('no-select');
|
||||
}, {once: true});
|
||||
|
||||
cancelSelection();
|
||||
cancelEvent(e as any);
|
||||
let bubble = findUpClassName(e.target, 'bubble');
|
||||
//cancelEvent(e as any);
|
||||
const bubble = findUpClassName(e.target, 'album-item') || findUpClassName(e.target, 'bubble');
|
||||
if(bubble) {
|
||||
appImManager.chatSelection.toggleByBubble(bubble);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||
import type { AppImManager } from "../../lib/appManagers/appImManager";
|
||||
import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager";
|
||||
import { cancelEvent, cancelSelection, findUpClassName, getSelectedText } from "../../helpers/dom";
|
||||
import { blurActiveElement, cancelEvent, cancelSelection, findUpClassName, getSelectedText } from "../../helpers/dom";
|
||||
import Button from "../button";
|
||||
import ButtonIcon from "../buttonIcon";
|
||||
import CheckboxField from "../checkbox";
|
||||
@ -225,6 +225,8 @@ export default class ChatSelection {
|
||||
}
|
||||
}
|
||||
|
||||
blurActiveElement(); // * for mobile keyboards
|
||||
|
||||
SetTransition(bubblesContainer, 'is-selecting', !!this.selectedMids.size, 200, () => {
|
||||
if(!this.isSelecting) {
|
||||
this.selectionContainer.remove();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Countries, { Country, PhoneCodesMain } from "../countries";
|
||||
import { cancelEvent } from "../helpers/dom";
|
||||
import { cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
import { clamp } from "../helpers/number";
|
||||
import { isTouchSupported } from "../helpers/touchSupport";
|
||||
@ -148,7 +148,7 @@ export const closeBtnMenu = () => {
|
||||
window.removeEventListener('contextmenu', onClick);
|
||||
}
|
||||
|
||||
document.removeEventListener('click', onClick);
|
||||
document.removeEventListener(CLICK_EVENT_NAME, onClick);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
@ -186,18 +186,21 @@ export function openBtnMenu(menuElement: HTMLElement, onClose?: () => void) {
|
||||
}
|
||||
|
||||
// ! safari iOS doesn't handle window click event on overlay, idk why
|
||||
document.addEventListener('click', onClick);
|
||||
document.addEventListener(CLICK_EVENT_NAME, onClick);
|
||||
}
|
||||
|
||||
const PADDING_TOP = 8;
|
||||
const PADDING_LEFT = 8;
|
||||
export function positionMenu({clientX, clientY}: {clientX: number, clientY: number}/* e: MouseEvent */, elem: HTMLElement, side?: 'left' | 'right' | 'center') {
|
||||
export function positionMenu({pageX, pageY}: MouseEvent | Touch, elem: HTMLElement, side?: 'left' | 'right' | 'center') {
|
||||
//let {clientX, clientY} = e;
|
||||
|
||||
// * side mean the OPEN side
|
||||
|
||||
let {scrollWidth: menuWidth, scrollHeight: menuHeight} = elem;
|
||||
let {innerWidth: windowWidth, innerHeight: windowHeight} = window;
|
||||
//let {innerWidth: windowWidth, innerHeight: windowHeight} = window;
|
||||
const rect = document.body.getBoundingClientRect();
|
||||
const windowWidth = rect.width;
|
||||
const windowHeight = rect.height;
|
||||
|
||||
side = mediaSizes.isMobile ? 'right' : 'left';
|
||||
let verticalSide: 'top' /* | 'bottom' */ | 'center' = 'top';
|
||||
@ -205,17 +208,17 @@ export function positionMenu({clientX, clientY}: {clientX: number, clientY: numb
|
||||
const getSides = () => {
|
||||
return {
|
||||
x: {
|
||||
left: clientX,
|
||||
right: clientX - menuWidth
|
||||
left: pageX,
|
||||
right: pageX - menuWidth
|
||||
},
|
||||
intermediateX: side == 'right' ? PADDING_LEFT : windowWidth - menuWidth - PADDING_LEFT,
|
||||
//intermediateX: clientX < windowWidth / 2 ? PADDING_LEFT : windowWidth - menuWidth - PADDING_LEFT,
|
||||
y: {
|
||||
top: clientY,
|
||||
bottom: clientY - menuHeight
|
||||
top: pageY,
|
||||
bottom: pageY - menuHeight
|
||||
},
|
||||
//intermediateY: verticalSide == 'top' ? PADDING_TOP : windowHeight - menuHeight - PADDING_TOP,
|
||||
intermediateY: clientY < windowHeight / 2 ? PADDING_TOP : windowHeight - menuHeight - PADDING_TOP,
|
||||
intermediateY: pageY < windowHeight / 2 ? PADDING_TOP : windowHeight - menuHeight - PADDING_TOP,
|
||||
};
|
||||
};
|
||||
|
||||
@ -290,11 +293,13 @@ export function attachContextMenuListener(element: HTMLElement, callback: (e: To
|
||||
if(isApple && isTouchSupported) {
|
||||
let timeout: number;
|
||||
|
||||
const options: any = /* null */{capture: true};
|
||||
|
||||
const onCancel = () => {
|
||||
clearTimeout(timeout);
|
||||
element.removeEventListener('touchmove', onCancel);
|
||||
element.removeEventListener('touchend', onCancel);
|
||||
element.removeEventListener('touchcancel', onCancel);
|
||||
element.removeEventListener('touchmove', onCancel, options);
|
||||
element.removeEventListener('touchend', onCancel, options);
|
||||
element.removeEventListener('touchcancel', onCancel, options);
|
||||
};
|
||||
|
||||
element.addEventListener('touchstart', (e) => {
|
||||
@ -303,11 +308,12 @@ export function attachContextMenuListener(element: HTMLElement, callback: (e: To
|
||||
return;
|
||||
}
|
||||
|
||||
element.addEventListener('touchmove', onCancel);
|
||||
element.addEventListener('touchend', onCancel);
|
||||
element.addEventListener('touchcancel', onCancel);
|
||||
element.addEventListener('touchmove', onCancel, options);
|
||||
element.addEventListener('touchend', onCancel, options);
|
||||
element.addEventListener('touchcancel', onCancel, options);
|
||||
|
||||
timeout = window.setTimeout(() => {
|
||||
element.addEventListener('touchend', cancelEvent, {once: true}); // * fix instant closing
|
||||
callback(e.touches[0]);
|
||||
onCancel();
|
||||
}, .4e3);
|
||||
|
@ -5,7 +5,7 @@ import appPollsManager, { Poll, PollResults } from "../lib/appManagers/appPollsM
|
||||
import serverTimeManager from "../lib/mtproto/serverTimeManager";
|
||||
import { RichTextProcessor } from "../lib/richtextprocessor";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import { cancelEvent, findUpClassName } from "../helpers/dom";
|
||||
import { cancelEvent, CLICK_EVENT_NAME, findUpClassName } from "../helpers/dom";
|
||||
import { ripple } from "./ripple";
|
||||
import appSidebarRight from "./sidebarRight";
|
||||
|
||||
@ -338,7 +338,8 @@ export default class PollElement extends HTMLElement {
|
||||
this.votersCountDiv.classList.add('hide');
|
||||
}
|
||||
|
||||
this.sendVoteBtn.addEventListener('click', () => {
|
||||
this.sendVoteBtn.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
/* const indexes = this.answerDivs.filter(el => el.classList.contains('is-chosing')).map(el => +el.dataset.index);
|
||||
if(indexes.length) {
|
||||
|
||||
@ -363,7 +364,7 @@ export default class PollElement extends HTMLElement {
|
||||
this.performResults(results, poll.chosenIndexes);
|
||||
} else if(!this.isClosed) {
|
||||
this.setVotersCount(results);
|
||||
this.addEventListener('click', this.clickHandler);
|
||||
this.addEventListener(CLICK_EVENT_NAME, this.clickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,7 +406,7 @@ export default class PollElement extends HTMLElement {
|
||||
this.descDiv.append(toggleHint);
|
||||
|
||||
//let active = false;
|
||||
toggleHint.addEventListener('click', (e) => {
|
||||
toggleHint.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
|
||||
//active = true;
|
||||
@ -425,12 +426,13 @@ export default class PollElement extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
clickHandler(e: MouseEvent) {
|
||||
clickHandler(e: Event) {
|
||||
const target = findUpClassName(e.target, 'poll-answer') as HTMLElement;
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
cancelEvent(e);
|
||||
const answerIndex = +target.dataset.index;
|
||||
if(this.isMultiple) {
|
||||
target.classList.toggle('is-chosing');
|
||||
@ -512,9 +514,9 @@ export default class PollElement extends HTMLElement {
|
||||
this.chosenIndexes = chosenIndexes.slice();
|
||||
|
||||
if(this.isRetracted) {
|
||||
this.addEventListener('click', this.clickHandler);
|
||||
this.addEventListener(CLICK_EVENT_NAME, this.clickHandler);
|
||||
} else {
|
||||
this.removeEventListener('click', this.clickHandler);
|
||||
this.removeEventListener(CLICK_EVENT_NAME, this.clickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import rootScope from "../lib/rootScope";
|
||||
import { cancelEvent, findUpClassName } from "../helpers/dom";
|
||||
import { blurActiveElement, cancelEvent, findUpClassName } from "../helpers/dom";
|
||||
import { ripple } from "./ripple";
|
||||
|
||||
export class PopupElement {
|
||||
@ -100,6 +100,7 @@ export class PopupElement {
|
||||
};
|
||||
|
||||
public show() {
|
||||
blurActiveElement(); // * hide mobile keyboard
|
||||
document.body.append(this.element);
|
||||
void this.element.offsetWidth; // reflow
|
||||
this.element.classList.add('active');
|
||||
|
@ -8,7 +8,7 @@ import appStateManager from "../../lib/appManagers/appStateManager";
|
||||
import appUsersManager from "../../lib/appManagers/appUsersManager";
|
||||
import { MOUNT_CLASS_TO } from "../../lib/mtproto/mtproto_config";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
import { findUpClassName, findUpTag } from "../../helpers/dom";
|
||||
import { CLICK_EVENT_NAME, findUpClassName, findUpTag } from "../../helpers/dom";
|
||||
import AppSearch, { SearchGroup } from "../appSearch";
|
||||
import "../avatar";
|
||||
import { parseMenuButtonsTo, putPreloader } from "../misc";
|
||||
@ -292,32 +292,32 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
|
||||
this.archivedCount = this.buttons.archived.querySelector('.archived-count') as HTMLSpanElement;
|
||||
|
||||
this.buttons.saved.addEventListener('click', (e) => {
|
||||
this.buttons.saved.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
///////this.log('savedbtn click');
|
||||
setTimeout(() => { // menu doesn't close if no timeout (lol)
|
||||
appImManager.setPeer(appImManager.myID);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
this.buttons.archived.addEventListener('click', (e) => {
|
||||
this.buttons.archived.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
this.selectTab(AppSidebarLeft.SLIDERITEMSIDS.archived);
|
||||
});
|
||||
|
||||
this.buttons.contacts.addEventListener('click', (e) => {
|
||||
this.buttons.contacts.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
this.contactsTab.openContacts();
|
||||
});
|
||||
|
||||
this.buttons.settings.addEventListener('click', (e) => {
|
||||
this.buttons.settings.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
this.settingsTab.fillElements();
|
||||
this.selectTab(AppSidebarLeft.SLIDERITEMSIDS.settings);
|
||||
});
|
||||
|
||||
this.newButtons.channel.addEventListener('click', (e) => {
|
||||
this.newButtons.channel.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
this.selectTab(AppSidebarLeft.SLIDERITEMSIDS.newChannel);
|
||||
});
|
||||
|
||||
[this.newButtons.group, this.buttons.newGroup].forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
btn.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
this.addMembersTab.init(0, 'chat', false, (peerIDs) => {
|
||||
this.newGroupTab.init(peerIDs);
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ import appMessagesManager from '../lib/appManagers/appMessagesManager';
|
||||
import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager';
|
||||
import LottieLoader from '../lib/lottieLoader';
|
||||
import VideoPlayer from '../lib/mediaPlayer';
|
||||
import { isInDOM } from "../helpers/dom";
|
||||
import { cancelEvent, CLICK_EVENT_NAME, isInDOM } from "../helpers/dom";
|
||||
import webpWorkerController from '../lib/webp/webpWorkerController';
|
||||
import animationIntersector from './animationIntersector';
|
||||
import appMediaPlaybackController from './appMediaPlaybackController';
|
||||
@ -373,7 +373,8 @@ export function wrapDocument(doc: MyDocument, withTime = false, uploading = fals
|
||||
let preloader: ProgressivePreloader;
|
||||
let download: DownloadBlob;
|
||||
|
||||
docDiv.addEventListener('click', (e) => {
|
||||
docDiv.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
if(!download) {
|
||||
if(downloadDiv.classList.contains('downloading')) {
|
||||
return; // means not ready yet
|
||||
@ -670,7 +671,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
}, true);
|
||||
|
||||
if(emoji) {
|
||||
div.addEventListener('click', () => {
|
||||
div.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
let animation = LottieLoader.getAnimation(div);
|
||||
|
||||
if(animation.paused) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config";
|
||||
import { isTouchSupported } from "./touchSupport";
|
||||
|
||||
/* export function isInDOM(element: Element, parentNode?: HTMLElement): boolean {
|
||||
if(!element) {
|
||||
@ -66,6 +67,38 @@ export function placeCaretAtEnd(el: HTMLElement) {
|
||||
}
|
||||
}
|
||||
|
||||
/* export function getFieldSelection(field: any) {
|
||||
if(field.selectionStart) {
|
||||
return field.selectionStart;
|
||||
// @ts-ignore
|
||||
} else if(!document.selection) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const c = '\x01';
|
||||
// @ts-ignore
|
||||
const sel = document.selection.createRange();
|
||||
const txt = sel.text;
|
||||
const dup = sel.duplicate();
|
||||
let len = 0;
|
||||
|
||||
try {
|
||||
dup.moveToElementText(field);
|
||||
} catch(e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sel.text = txt + c;
|
||||
len = dup.text.indexOf(c);
|
||||
sel.moveStart('character', -1);
|
||||
sel.text = '';
|
||||
|
||||
// if (browser.msie && len == -1) {
|
||||
// return field.value.length
|
||||
// }
|
||||
return len;
|
||||
} */
|
||||
|
||||
export function getRichValue(field: HTMLElement) {
|
||||
if(!field) {
|
||||
return '';
|
||||
@ -90,6 +123,15 @@ MOUNT_CLASS_TO && (MOUNT_CLASS_TO.getRichValue = getRichValue);
|
||||
const markdownTags = [{
|
||||
tagName: 'STRONG',
|
||||
markdown: '**'
|
||||
}, {
|
||||
tagName: 'B', // * legacy (Ctrl+B)
|
||||
markdown: '**'
|
||||
}, {
|
||||
tagName: 'U', // * legacy (Ctrl+I)
|
||||
markdown: '_-_'
|
||||
}, {
|
||||
tagName: 'I', // * legacy (Ctrl+I)
|
||||
markdown: '__'
|
||||
}, {
|
||||
tagName: 'EM',
|
||||
markdown: '__'
|
||||
@ -126,7 +168,7 @@ export function getRichElementValue(node: HTMLElement, lines: string[], line: st
|
||||
}
|
||||
}
|
||||
|
||||
line.push(markdown && node.nodeValue.trim() ? markdown + node.nodeValue + markdown : node.nodeValue);
|
||||
line.push(markdown && node.nodeValue.trim() ? '\x01' + markdown + node.nodeValue + markdown + '\x01' : node.nodeValue);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -392,3 +434,11 @@ export function getSelectedText(): string {
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
export function blurActiveElement() {
|
||||
if(document.activeElement && (document.activeElement as HTMLInputElement).blur) {
|
||||
(document.activeElement as HTMLInputElement).blur();
|
||||
}
|
||||
}
|
||||
|
||||
export const CLICK_EVENT_NAME = isTouchSupported ? 'touchend' : 'click';
|
||||
|
@ -569,7 +569,7 @@ export class ApiUpdatesManager {
|
||||
} else {
|
||||
// ! for testing
|
||||
/* state.seq = 1;
|
||||
state.pts = state.pts - 100;
|
||||
state.pts = state.pts - 1000;
|
||||
state.date = 1; */
|
||||
|
||||
Object.assign(this.updatesState, state);
|
||||
|
@ -202,6 +202,7 @@ export class AppDialogsManager {
|
||||
//private topOffsetIndex = 0;
|
||||
|
||||
private sliceTimeout: number;
|
||||
private reorderDialogsTimeout: number;
|
||||
|
||||
constructor() {
|
||||
this.chatsPreloader = putPreloader(null, true);
|
||||
@ -891,37 +892,40 @@ export class AppDialogsManager {
|
||||
|
||||
private reorderDialogs() {
|
||||
//const perf = performance.now();
|
||||
|
||||
let offset = 0;
|
||||
if(this.topOffsetIndex) {
|
||||
const element = this.chatList.firstElementChild;
|
||||
if(element) {
|
||||
const peerID = +element.getAttribute('data-peerID');
|
||||
const firstDialog = appMessagesManager.getDialogByPeerID(peerID);
|
||||
offset = firstDialog[1];
|
||||
}
|
||||
}
|
||||
|
||||
const dialogs = appMessagesManager.dialogsStorage.getFolder(this.filterID);
|
||||
const currentOrder = (Array.from(this.chatList.children) as HTMLElement[]).map(el => +el.getAttribute('data-peerID'));
|
||||
|
||||
dialogs.forEach((dialog, index) => {
|
||||
const dom = this.getDialogDom(dialog.peerID);
|
||||
if(!dom) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentIndex = currentOrder[dialog.peerID];
|
||||
const needIndex = index - offset;
|
||||
|
||||
if(currentIndex != needIndex) {
|
||||
if(positionElementByIndex(dom.listEl, this.chatList, needIndex)) {
|
||||
this.log.debug('setDialogPosition:', dialog, dom, needIndex);
|
||||
if(this.reorderDialogsTimeout) return;
|
||||
this.reorderDialogsTimeout = window.requestAnimationFrame(() => {
|
||||
this.reorderDialogsTimeout = 0;
|
||||
let offset = 0;
|
||||
if(this.topOffsetIndex) {
|
||||
const element = this.chatList.firstElementChild;
|
||||
if(element) {
|
||||
const peerID = +element.getAttribute('data-peerID');
|
||||
const firstDialog = appMessagesManager.getDialogByPeerID(peerID);
|
||||
offset = firstDialog[1];
|
||||
}
|
||||
}
|
||||
|
||||
const dialogs = appMessagesManager.dialogsStorage.getFolder(this.filterID);
|
||||
const currentOrder = (Array.from(this.chatList.children) as HTMLElement[]).map(el => +el.getAttribute('data-peerID'));
|
||||
|
||||
dialogs.forEach((dialog, index) => {
|
||||
const dom = this.getDialogDom(dialog.peerID);
|
||||
if(!dom) {
|
||||
return;
|
||||
}
|
||||
|
||||
const needIndex = index - offset;
|
||||
const peerIDByIndex = currentOrder[needIndex];
|
||||
|
||||
if(peerIDByIndex != dialog.peerID) {
|
||||
if(positionElementByIndex(dom.listEl, this.chatList, needIndex)) {
|
||||
this.log.debug('setDialogPosition:', dialog, dom, peerIDByIndex, needIndex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//this.log('Reorder time:', performance.now() - perf);
|
||||
});
|
||||
|
||||
//this.log('Reorder time:', performance.now() - perf);
|
||||
}
|
||||
|
||||
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) {
|
||||
|
@ -40,7 +40,7 @@ import apiManager from '../mtproto/mtprotoworker';
|
||||
import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config';
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import rootScope from '../rootScope';
|
||||
import { cancelEvent, findUpClassName, findUpTag, placeCaretAtEnd, whichChild } from "../../helpers/dom";
|
||||
import { cancelEvent, CLICK_EVENT_NAME, findUpClassName, findUpTag, placeCaretAtEnd, whichChild } from "../../helpers/dom";
|
||||
import apiUpdatesManager from './apiUpdatesManager';
|
||||
import appChatsManager, { Channel, Chat } from "./appChatsManager";
|
||||
import appDialogsManager from "./appDialogsManager";
|
||||
@ -708,7 +708,7 @@ export class AppImManager {
|
||||
this.mutePeer(this.peerID);
|
||||
});
|
||||
|
||||
this.btnJoin.addEventListener('click', (e) => {
|
||||
this.btnJoin.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
|
||||
this.btnJoin.setAttribute('disabled', 'true');
|
||||
@ -717,11 +717,11 @@ export class AppImManager {
|
||||
});
|
||||
});
|
||||
|
||||
this.menuButtons.mute.addEventListener('click', (e) => {
|
||||
this.menuButtons.mute.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
this.mutePeer(this.peerID);
|
||||
});
|
||||
|
||||
this.menuButtons.search.addEventListener('click', (e) => {
|
||||
this.menuButtons.search.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
new ChatSearch();
|
||||
});
|
||||
|
||||
@ -745,6 +745,12 @@ export class AppImManager {
|
||||
this.chatInputC.replyElements.cancelBtn.click();
|
||||
} else if(this.peerID != 0) { // hide current dialog
|
||||
this.setPeer(0);
|
||||
cancelEvent(e);
|
||||
}
|
||||
|
||||
// * cancel event for safari, because if application is in fullscreen, browser will try to exit fullscreen
|
||||
if(this.peerID) {
|
||||
cancelEvent(e);
|
||||
}
|
||||
} else if(e.key == 'Meta' || e.key == 'Control') {
|
||||
return;
|
||||
@ -785,7 +791,8 @@ export class AppImManager {
|
||||
|
||||
document.body.addEventListener('keydown', onKeyDown);
|
||||
|
||||
this.goDownBtn.addEventListener('click', () => {
|
||||
this.goDownBtn.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
const dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
|
||||
|
||||
if(dialog) {
|
||||
@ -1898,7 +1905,8 @@ export class AppImManager {
|
||||
containerDiv.append(rowDiv);
|
||||
});
|
||||
|
||||
containerDiv.addEventListener('click', (e) => {
|
||||
containerDiv.addEventListener(CLICK_EVENT_NAME, (e) => {
|
||||
cancelEvent(e);
|
||||
let target = e.target as HTMLElement;
|
||||
|
||||
if(!target.classList.contains('reply-markup-button')) target = findUpClassName(target, 'reply-markup-button');
|
||||
@ -2790,8 +2798,13 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
public setMutedState(muted = false) {
|
||||
appSidebarRight.sharedMediaTab.profileElements.notificationsCheckbox.checked = !muted;
|
||||
appSidebarRight.sharedMediaTab.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
|
||||
if(!this.peerID) return;
|
||||
|
||||
const profileElements = appSidebarRight.sharedMediaTab.profileElements;
|
||||
if(profileElements) {
|
||||
appSidebarRight.sharedMediaTab.profileElements.notificationsCheckbox.checked = !muted;
|
||||
appSidebarRight.sharedMediaTab.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
|
||||
}
|
||||
|
||||
if(appPeersManager.isBroadcast(this.peerID)) { // not human
|
||||
this.btnMute.classList.remove('tgico-mute', 'tgico-unmute');
|
||||
|
@ -87,7 +87,7 @@ Utility Classes
|
||||
}
|
||||
|
||||
// No Text Select
|
||||
.no-select {
|
||||
.no-select/* , .no-select * */ {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ avatar-element {
|
||||
/* overflow: hidden; */
|
||||
position: relative;
|
||||
user-select: none;
|
||||
text-transform: uppercase;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
font-size: 14px;
|
||||
|
@ -106,6 +106,10 @@
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
&.bottom-center {
|
||||
transform-origin: top center;
|
||||
}
|
||||
|
||||
&.top-left {
|
||||
top: initial;
|
||||
right: 0;
|
||||
@ -120,6 +124,14 @@
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
&.center-left {
|
||||
transform-origin: center right;
|
||||
}
|
||||
|
||||
&.center-right {
|
||||
transform-origin: center left;
|
||||
}
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
position: relative;
|
||||
@ -170,6 +182,7 @@
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
//background-color: rgba(0, 0, 0, .2);
|
||||
}
|
||||
|
||||
|
@ -229,6 +229,12 @@ $bubble-margin: .25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#bubbles.is-selecting & {
|
||||
.audio, .document, .attachment, poll-element {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
//min-width: 60px;
|
||||
@ -244,7 +250,12 @@ $bubble-margin: .25rem;
|
||||
height: fit-content;
|
||||
z-index: 2;
|
||||
transition: .2s transform;
|
||||
user-select: text;
|
||||
user-select: none;
|
||||
|
||||
html.no-touch #bubbles:not(.is-selecting) &,
|
||||
html.is-touch #bubbles.is-selecting:not(.no-select) & {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
max-width: 85%;
|
||||
|
@ -70,7 +70,7 @@
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
}
|
||||
/* font-weight: 500; */
|
||||
|
||||
|
@ -214,12 +214,9 @@
|
||||
max-width: 78px;
|
||||
width: 78px;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
padding: 12px 0 0 !important;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
@ -233,6 +230,10 @@
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.dialog-title-details {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-caption {
|
||||
max-width: 65px;
|
||||
padding: 2px 0px 9px;
|
||||
@ -243,10 +244,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.user-title {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.search-group-scrollable {
|
||||
position: relative;
|
||||
|
||||
@ -595,10 +592,6 @@
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.input-field input {
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-left-h2 {
|
||||
|
@ -198,6 +198,38 @@ $messages-container-width: 728px;
|
||||
unicode-range:U + 0000-00FF, U + 0131, U + 0152-0153, U + 02BB-02BC, U + 02C6, U + 02DA, U + 02DC, U + 2000-206F, U + 2074, U + 20AC, U + 2122, U + 2191, U + 2193, U + 2212, U + 2215, U + FEFF, U + FFFD
|
||||
}
|
||||
|
||||
// !!! FIX FOR [contenteditable] Ctrl+B, due to font-weight: 500;
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(assets/fonts/KFOlCnqEu92Fr1MmEU9fABc4AMP6lbBP.woff2) format('woff2');
|
||||
unicode-range:U + 0400-045F, U + 0490-0491, U + 04B0-04B1, U + 2116
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(assets/fonts/KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2) format('woff2');
|
||||
unicode-range:U + 0100-024F, U + 0259, U + 1E00-1EFF, U + 2020, U + 20A0-20AB, U + 20AD-20CF, U + 2113, U + 2C60-2C7F, U + A720-A7FF
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(assets/fonts/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2) format('woff2');
|
||||
unicode-range:U + 0000-00FF, U + 0131, U + 0152-0153, U + 02BB-02BC, U + 02C6, U + 02DA, U + 02DC, U + 2000-206F, U + 2074, U + 20AC, U + 2122, U + 2191, U + 2193, U + 2212, U + 2215, U + FEFF, U + FFFD
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@ -404,7 +436,8 @@ hr {
|
||||
|
||||
.user-title, b/* , .user-last-message b */ {
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
font-weight: bolder;
|
||||
//font-weight: 500;
|
||||
//font-weight: normal;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user