Sorting folders
This commit is contained in:
parent
a88043f966
commit
beabcb2a23
|
@ -366,7 +366,7 @@ export default class AppMediaViewerBase<
|
|||
if(enable) {
|
||||
if(!this.zoomSwipeHandler) {
|
||||
let lastDiffX: number, lastDiffY: number;
|
||||
const multiplier = -1;
|
||||
const multiplier = 1;
|
||||
this.zoomSwipeHandler = new SwipeHandler({
|
||||
element: this.moversContainer,
|
||||
onFirstSwipe: () => {
|
||||
|
|
|
@ -382,6 +382,8 @@ export default class AppSearchSuper {
|
|||
this.swipeHandler = handleTabSwipe({
|
||||
element: this.tabsContainer,
|
||||
onSwipe: (xDiff, yDiff, e) => {
|
||||
xDiff *= -1;
|
||||
yDiff *= -1;
|
||||
const prevId = this.selectTab.prevId();
|
||||
const children = Array.from(this.tabsMenu.children) as HTMLElement[];
|
||||
let idx: number;
|
||||
|
|
|
@ -909,7 +909,9 @@ export default class ChatBubbles {
|
|||
|
||||
return !!target;
|
||||
},
|
||||
onSwipe: (xDiff, yDiff) => {
|
||||
onSwipe: (xDiff) => {
|
||||
xDiff *= -1;
|
||||
|
||||
shouldReply = xDiff >= replyAfter;
|
||||
|
||||
if(shouldReply && !icon.classList.contains('is-visible')) {
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class MovableElement extends EventListenerBase<{
|
|||
private minWidth: number;
|
||||
private minHeight: number;
|
||||
private element: HTMLElement;
|
||||
private verifyTouchTarget: (e: TouchEvent | MouseEvent) => boolean;
|
||||
private verifyTouchTarget: SwipeHandler['verifyTouchTarget'];
|
||||
|
||||
private top: number;
|
||||
private left: number;
|
||||
|
@ -96,8 +96,6 @@ export default class MovableElement extends EventListenerBase<{
|
|||
const swipeHandler = this.swipeHandler = new SwipeHandler({
|
||||
element: this.element,
|
||||
onSwipe: (xDiff, yDiff, e) => {
|
||||
xDiff *= -1; // to right will be positive
|
||||
yDiff *= -1; // to bottom will be positive
|
||||
// console.log(xDiff, yDiff, e);
|
||||
|
||||
if(resizingSide) {
|
||||
|
|
|
@ -170,6 +170,9 @@ export default class PeerProfileAvatars {
|
|||
const swipeHandler = this.swipeHandler = new SwipeHandler({
|
||||
element: this.avatars,
|
||||
onSwipe: (xDiff, yDiff) => {
|
||||
xDiff *= -1;
|
||||
yDiff *= -1;
|
||||
|
||||
lastDiffX = xDiff;
|
||||
let lastX = x + xDiff * -PeerProfileAvatars.SCALE;
|
||||
if(lastX > 0) lastX = 0;
|
||||
|
@ -182,7 +185,7 @@ export default class PeerProfileAvatars {
|
|||
verifyTouchTarget: (e) => {
|
||||
if(!checkScrollTop()) {
|
||||
cancelNextClick();
|
||||
cancelEvent(e);
|
||||
cancelEvent(e as any as Event);
|
||||
return false;
|
||||
} else if(this.container.classList.contains('is-single') || freeze) {
|
||||
return false;
|
||||
|
|
|
@ -291,6 +291,17 @@ export default class Row {
|
|||
this.container.classList.remove('is-disabled');
|
||||
});
|
||||
}
|
||||
|
||||
public makeSortable() {
|
||||
const sortIcon = document.createElement('span');
|
||||
this.container.classList.add('row-sortable', 'tgico');
|
||||
sortIcon.classList.add('row-sortable-icon', 'tgico-menu');
|
||||
this.container.append(sortIcon);
|
||||
}
|
||||
|
||||
public toggleSorting(enabled?: boolean) {
|
||||
this.container.classList.toggle('cant-sort', !enabled);
|
||||
}
|
||||
}
|
||||
|
||||
export const CreateRowFromCheckboxField = (checkboxField: CheckboxField) => {
|
||||
|
|
|
@ -23,6 +23,9 @@ import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
|
|||
import {FOLDER_ID_ALL, FOLDER_ID_ARCHIVE, REAL_FOLDERS} from '../../../lib/mtproto/mtproto_config';
|
||||
import replaceContent from '../../../helpers/dom/replaceContent';
|
||||
import SettingSection from '../../settingSection';
|
||||
import Sortable from '../../../helpers/dom/sortable';
|
||||
import whichChild from '../../../helpers/dom/whichChild';
|
||||
import indexOfAndSplice from '../../../helpers/array/indexOfAndSplice';
|
||||
|
||||
export default class AppChatFoldersTab extends SliderSuperTab {
|
||||
private createFolderBtn: HTMLElement;
|
||||
|
@ -30,6 +33,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||
private suggestedSection: SettingSection;
|
||||
private stickerContainer: HTMLElement;
|
||||
private animation: RLottiePlayer;
|
||||
private list: HTMLElement;
|
||||
|
||||
private filtersRendered: {[filterId: number]: Row} = {};
|
||||
private loadAnimationPromise: ReturnType<LottieLoader['waitForFirstFrame']>;
|
||||
|
@ -91,7 +95,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||
row = new Row({
|
||||
title: filter.id === FOLDER_ID_ALL ? i18n('FilterAllChats') : wrapEmojiText(filter.title),
|
||||
subtitle: description,
|
||||
clickable: filter.id !== FOLDER_ID_ALL,
|
||||
clickable: true,
|
||||
buttonRightLangKey: dialogFilter._ === 'dialogFilterSuggested' ? 'Add' : undefined
|
||||
});
|
||||
|
||||
|
@ -108,6 +112,8 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||
}
|
||||
|
||||
this.filtersRendered[filter.id] = row;
|
||||
|
||||
row.makeSortable();
|
||||
}
|
||||
} else {
|
||||
if(filter.id !== FOLDER_ID_ALL) {
|
||||
|
@ -156,6 +162,9 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||
});
|
||||
this.foldersSection.container.style.display = 'none';
|
||||
|
||||
this.list = document.createElement('div');
|
||||
this.foldersSection.content.append(this.list);
|
||||
|
||||
this.suggestedSection = new SettingSection({
|
||||
name: 'FilterRecommended'
|
||||
});
|
||||
|
@ -181,7 +190,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||
continue;
|
||||
}
|
||||
|
||||
await this.renderFolder(filter, this.foldersSection.content, undefined, true);
|
||||
await this.renderFolder(filter, this.list, undefined, true);
|
||||
}
|
||||
|
||||
this.toggleAllChats();
|
||||
|
@ -194,7 +203,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||
if(filterRendered) {
|
||||
await this.renderFolder(filter, null, filterRendered);
|
||||
} else if(filter.id !== FOLDER_ID_ARCHIVE) {
|
||||
await this.renderFolder(filter, this.foldersSection.content, undefined, true);
|
||||
await this.renderFolder(filter, this.list, undefined, true);
|
||||
}
|
||||
|
||||
onFiltersContainerUpdate();
|
||||
|
@ -245,6 +254,28 @@ export default class AppChatFoldersTab extends SliderSuperTab {
|
|||
return lottieLoader.waitForFirstFrame(player);
|
||||
});
|
||||
|
||||
new Sortable({
|
||||
list: this.list,
|
||||
middleware: this.middlewareHelper.get(),
|
||||
onSort: (prevIdx, newIdx) => {
|
||||
let order: number[] = [];
|
||||
for(const filterId in this.filtersRendered) {
|
||||
const row = this.filtersRendered[filterId];
|
||||
const idx = whichChild(row.container);
|
||||
order[idx] = +filterId;
|
||||
}
|
||||
|
||||
order = order.filter((filterId) => filterId !== undefined);
|
||||
if(!rootScope.premium) {
|
||||
indexOfAndSplice(order, FOLDER_ID_ALL);
|
||||
// order.unshift(FOLDER_ID_ALL);
|
||||
}
|
||||
|
||||
this.managers.filtersStorage.updateDialogFiltersOrder(order);
|
||||
},
|
||||
scrollable: this.scrollable
|
||||
});
|
||||
|
||||
this.getSuggestedFilters();
|
||||
|
||||
/* return Promise.all([
|
||||
|
|
|
@ -12,8 +12,19 @@ import {Middleware} from '../helpers/middleware';
|
|||
import ListenerSetter, {ListenerOptions} from '../helpers/listenerSetter';
|
||||
import {attachContextMenuListener} from '../helpers/dom/attachContextMenuListener';
|
||||
|
||||
const getEvent = (e: TouchEvent | MouseEvent) => {
|
||||
return (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent;
|
||||
type E = {
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
target: EventTarget,
|
||||
button?: number
|
||||
};
|
||||
|
||||
type EE = E | (Exclude<E, 'clientX' | 'clientY'> & {
|
||||
touches: E[]
|
||||
});
|
||||
|
||||
const getEvent = (e: EE) => {
|
||||
return 'touches' in e ? e.touches[0] : e;
|
||||
};
|
||||
|
||||
const attachGlobalListenerTo = window;
|
||||
|
@ -29,6 +40,7 @@ export type SwipeHandlerOptions = {
|
|||
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
|
||||
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
|
||||
onReset?: SwipeHandler['onReset'],
|
||||
onStart?: SwipeHandler['onStart'],
|
||||
cursor?: SwipeHandler['cursor'],
|
||||
cancelEvent?: SwipeHandler['cancelEvent'],
|
||||
listenerOptions?: SwipeHandler['listenerOptions'],
|
||||
|
@ -42,18 +54,22 @@ const MOUSE_MOVE_OPTIONS: ListenerOptions = false as any;
|
|||
|
||||
export default class SwipeHandler {
|
||||
private element: HTMLElement;
|
||||
private onSwipe: (xDiff: number, yDiff: number, e: TouchEvent | MouseEvent) => boolean | void;
|
||||
private verifyTouchTarget: (evt: TouchEvent | MouseEvent) => boolean | Promise<boolean>;
|
||||
private onFirstSwipe: (e: TouchEvent | MouseEvent) => void;
|
||||
private onSwipe: (xDiff: number, yDiff: number, e: EE) => boolean | void;
|
||||
private verifyTouchTarget: (evt: EE) => boolean | Promise<boolean>;
|
||||
private onFirstSwipe: (e: EE) => void;
|
||||
private onReset: () => void;
|
||||
private onStart: () => void;
|
||||
private cursor: 'grabbing' | 'move' | 'row-resize' | 'col-resize' | 'nesw-resize' | 'nwse-resize' | 'ne-resize' | 'se-resize' | 'sw-resize' | 'nw-resize' | 'n-resize' | 'e-resize' | 's-resize' | 'w-resize' | '' = 'grabbing';
|
||||
private cancelEvent = true;
|
||||
private listenerOptions: boolean | AddEventListenerOptions = false;
|
||||
private setCursorTo: HTMLElement;
|
||||
|
||||
private hadMove = false;
|
||||
private xDown: number = null;
|
||||
private yDown: number = null;
|
||||
private hadMove: boolean;
|
||||
private eventUp: E;
|
||||
private xDown: number;
|
||||
private yDown: number;
|
||||
private xAdded: number;
|
||||
private yAdded: number;
|
||||
|
||||
private withDelay: boolean;
|
||||
private listenerSetter: ListenerSetter;
|
||||
|
@ -65,6 +81,8 @@ export default class SwipeHandler {
|
|||
this.listenerSetter = new ListenerSetter();
|
||||
this.setListeners();
|
||||
|
||||
this.resetValues();
|
||||
|
||||
options.middleware?.onDestroy(() => {
|
||||
this.reset();
|
||||
this.removeListeners();
|
||||
|
@ -73,12 +91,15 @@ export default class SwipeHandler {
|
|||
|
||||
public setListeners() {
|
||||
if(!IS_TOUCH_SUPPORTED) {
|
||||
// @ts-ignore
|
||||
this.listenerSetter.add(this.element)('mousedown', this.handleStart, this.listenerOptions);
|
||||
this.listenerSetter.add(attachGlobalListenerTo)('mouseup', this.reset);
|
||||
} else {
|
||||
if(this.withDelay) {
|
||||
// @ts-ignore
|
||||
attachContextMenuListener(this.element, this.handleStart, this.listenerSetter);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this.listenerSetter.add(this.element)('touchstart', this.handleStart, this.listenerOptions);
|
||||
}
|
||||
|
||||
|
@ -98,7 +119,26 @@ export default class SwipeHandler {
|
|||
}
|
||||
}
|
||||
|
||||
reset = (e?: Event) => {
|
||||
public add(x: number, y: number) {
|
||||
this.xAdded = x;
|
||||
this.yAdded = y;
|
||||
this.handleMove({
|
||||
clientX: this.eventUp.clientX,
|
||||
clientY: this.eventUp.clientY,
|
||||
target: this.eventUp.target
|
||||
});
|
||||
}
|
||||
|
||||
protected resetValues() {
|
||||
this.hadMove = false;
|
||||
this.xAdded = this.yAdded = 0;
|
||||
this.xDown =
|
||||
this.yDown =
|
||||
this.eventUp =
|
||||
undefined;
|
||||
}
|
||||
|
||||
protected reset = (e?: Event) => {
|
||||
/* if(e) {
|
||||
cancelEvent(e);
|
||||
} */
|
||||
|
@ -114,42 +154,50 @@ export default class SwipeHandler {
|
|||
this.onReset?.();
|
||||
}
|
||||
|
||||
this.xDown = this.yDown = null;
|
||||
this.hadMove = false;
|
||||
this.resetValues();
|
||||
};
|
||||
|
||||
handleStart = async(_e: TouchEvent | MouseEvent) => {
|
||||
protected handleStart = async(_e: EE) => {
|
||||
const e = getEvent(_e);
|
||||
if(e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.verifyTouchTarget && !(await this.verifyTouchTarget(_e))) {
|
||||
return this.reset();
|
||||
}
|
||||
|
||||
this.xDown = e.clientX;
|
||||
this.yDown = e.clientY;
|
||||
this.eventUp = e;
|
||||
|
||||
if(IS_TOUCH_SUPPORTED) {
|
||||
// @ts-ignore
|
||||
this.listenerSetter.add(attachGlobalListenerTo)('touchmove', this.handleMove, TOUCH_MOVE_OPTIONS);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this.listenerSetter.add(attachGlobalListenerTo)('mousemove', this.handleMove, MOUSE_MOVE_OPTIONS);
|
||||
}
|
||||
|
||||
this.onStart?.();
|
||||
};
|
||||
|
||||
handleMove = (_e: TouchEvent | MouseEvent) => {
|
||||
if(this.xDown === null || this.yDown === null || RESET_GLOBAL) {
|
||||
protected handleMove = (_e: EE) => {
|
||||
if(this.xDown === undefined || this.yDown === undefined || RESET_GLOBAL) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.cancelEvent) {
|
||||
cancelEvent(_e);
|
||||
cancelEvent(_e as any);
|
||||
}
|
||||
|
||||
const e = getEvent(_e);
|
||||
const e = this.eventUp = getEvent(_e);
|
||||
const xUp = e.clientX;
|
||||
const yUp = e.clientY;
|
||||
|
||||
const xDiff = this.xDown - xUp;
|
||||
const yDiff = this.yDown - yUp;
|
||||
const xDiff = xUp - this.xDown + this.xAdded;
|
||||
const yDiff = yUp - this.yDown + this.yAdded;
|
||||
|
||||
if(!this.hadMove) {
|
||||
if(!xDiff && !yDiff) {
|
||||
|
@ -165,20 +213,6 @@ export default class SwipeHandler {
|
|||
this.onFirstSwipe?.(_e);
|
||||
}
|
||||
|
||||
// if(Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
|
||||
// if(xDiff > 0) { /* left swipe */
|
||||
|
||||
// } else { /* right swipe */
|
||||
|
||||
// }
|
||||
// } else {
|
||||
// if(yDiff > 0) { /* up swipe */
|
||||
|
||||
// } else { /* down swipe */
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
/* reset values */
|
||||
const onSwipeResult = this.onSwipe(xDiff, yDiff, _e);
|
||||
if(onSwipeResult !== undefined && onSwipeResult) {
|
||||
|
|
|
@ -4,18 +4,14 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import IS_TOUCH_SUPPORTED from '../environment/touchSupport';
|
||||
import cancelEvent from '../helpers/dom/cancelEvent';
|
||||
import {attachClickEvent} from '../helpers/dom/clickEvent';
|
||||
import findUpAsChild from '../helpers/dom/findUpAsChild';
|
||||
import placeCaretAtEnd from '../helpers/dom/placeCaretAtEnd';
|
||||
import positionElementByIndex from '../helpers/dom/positionElementByIndex';
|
||||
import whichChild from '../helpers/dom/whichChild';
|
||||
import Sortable from '../helpers/dom/sortable';
|
||||
import ListenerSetter from '../helpers/listenerSetter';
|
||||
import {Middleware} from '../helpers/middleware';
|
||||
import noop from '../helpers/noop';
|
||||
import clamp from '../helpers/number/clamp';
|
||||
import pause from '../helpers/schedulers/pause';
|
||||
import SortedList, {SortedElementBase} from '../helpers/sortedList';
|
||||
import {Chat, User, Username} from '../layer';
|
||||
import {i18n, LangPackKey} from '../lib/langPack';
|
||||
|
@ -23,7 +19,6 @@ import rootScope from '../lib/rootScope';
|
|||
import confirmationPopup from './confirmationPopup';
|
||||
import Row from './row';
|
||||
import SettingSection from './settingSection';
|
||||
import SwipeHandler from './swipeHandler';
|
||||
import {UsernameInputField} from './usernameInputField';
|
||||
|
||||
export default class UsernamesSection extends SettingSection {
|
||||
|
@ -83,10 +78,7 @@ export default class UsernamesSection extends SettingSection {
|
|||
const media = row.createMedia('medium');
|
||||
media.classList.add(CLASS_NAME + '-username-icon', 'tgico');
|
||||
|
||||
const sortIcon = document.createElement('span');
|
||||
row.container.classList.add('row-sortable', 'tgico');
|
||||
sortIcon.classList.add('row-sortable-icon', 'tgico-menu');
|
||||
row.container.append(sortIcon);
|
||||
row.makeSortable();
|
||||
|
||||
changeActive(row, active);
|
||||
|
||||
|
@ -99,7 +91,7 @@ export default class UsernamesSection extends SettingSection {
|
|||
const changeActive = (row: Row, active: boolean) => {
|
||||
row.subtitle.replaceChildren(i18n(row.container.dataset.editable ? 'UsernameLinkEditable' : (active ? 'UsernameLinkActive' : 'UsernameLinkInactive')));
|
||||
row.container.classList.toggle('active', active);
|
||||
row.container.classList.toggle('cant-sort', !active);
|
||||
row.toggleSorting(active);
|
||||
};
|
||||
|
||||
const applyUsernames = (usernames: Username[] = []) => {
|
||||
|
@ -197,112 +189,10 @@ export default class UsernamesSection extends SettingSection {
|
|||
});
|
||||
});
|
||||
|
||||
const getSortableTarget = (target: HTMLElement) => {
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
let child = findUpAsChild(target as HTMLElement, list);
|
||||
if(child && child.classList.contains('cant-sort')) {
|
||||
child = undefined;
|
||||
}
|
||||
|
||||
return child;
|
||||
};
|
||||
|
||||
let element: HTMLElement,
|
||||
elementRect: DOMRect,
|
||||
containerRect: DOMRect,
|
||||
minY: number,
|
||||
maxY: number,
|
||||
siblings: HTMLElement[];
|
||||
const swipeHandler = new SwipeHandler({
|
||||
element: list,
|
||||
onSwipe: (xDiff, yDiff) => {
|
||||
yDiff = clamp(-yDiff, minY, maxY);
|
||||
element.style.transform = `translateY(${yDiff}px)`;
|
||||
const count = Math.round(Math.abs(yDiff) / elementRect.height);
|
||||
const lastSiblings = siblings;
|
||||
siblings = [];
|
||||
const property = yDiff < 0 ? 'previousElementSibling' : 'nextElementSibling';
|
||||
let sibling = element[property] as HTMLElement;
|
||||
for(let i = 0; i < count; ++i) {
|
||||
if(getSortableTarget(sibling)) {
|
||||
siblings.push(sibling);
|
||||
sibling = sibling[property] as HTMLElement;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(lastSiblings || []).forEach((sibling) => {
|
||||
if(!siblings.includes(sibling)) {
|
||||
sibling.style.transform = '';
|
||||
}
|
||||
});
|
||||
|
||||
siblings.forEach((sibling) => {
|
||||
const y = elementRect.height * (yDiff < 0 ? 1 : -1);
|
||||
sibling.style.transform = `translateY(${y}px)`;
|
||||
});
|
||||
},
|
||||
verifyTouchTarget: (e) => {
|
||||
if(list.classList.contains('is-reordering')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
element = getSortableTarget(e.target as HTMLElement);
|
||||
return !!element/* && pause(150).then(() => true) */;
|
||||
},
|
||||
onFirstSwipe: () => {
|
||||
list.classList.add('is-reordering');
|
||||
element.classList.add('is-dragging', 'no-transition');
|
||||
swipeHandler.setCursor('grabbing');
|
||||
elementRect = element.getBoundingClientRect();
|
||||
containerRect = list.getBoundingClientRect();
|
||||
|
||||
minY = containerRect.top - elementRect.top;
|
||||
maxY = containerRect.bottom - elementRect.bottom;
|
||||
},
|
||||
onReset: async() => {
|
||||
const length = siblings.length;
|
||||
const move = length && length * (siblings[0].previousElementSibling === element ? 1 : -1);
|
||||
const idx = whichChild(element);
|
||||
const newIdx = idx + move;
|
||||
|
||||
element.classList.remove('no-transition');
|
||||
element.style.transform = move ? `translateY(${move * elementRect.height}px)` : '';
|
||||
swipeHandler.setCursor('');
|
||||
|
||||
if(!IS_TOUCH_SUPPORTED) {
|
||||
attachClickEvent(document.body, cancelEvent, {capture: true, once: true});
|
||||
}
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
await pause(250);
|
||||
}
|
||||
|
||||
list.classList.remove('is-reordering');
|
||||
element.classList.remove('is-dragging');
|
||||
positionElementByIndex(element, list, newIdx, idx);
|
||||
[element, ...siblings].forEach((element) => {
|
||||
element.style.transform = '';
|
||||
});
|
||||
|
||||
element =
|
||||
siblings =
|
||||
elementRect =
|
||||
containerRect =
|
||||
minY =
|
||||
maxY =
|
||||
undefined;
|
||||
|
||||
// cancelClick = true;
|
||||
|
||||
if(!move) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Sortable({
|
||||
list,
|
||||
middleware,
|
||||
onSort: (idx, newIdx) => {
|
||||
const username = _usernames.splice(idx, 1)[0];
|
||||
_usernames.splice(newIdx, 0, username);
|
||||
sortedList.updateList();
|
||||
|
@ -315,10 +205,7 @@ export default class UsernamesSection extends SettingSection {
|
|||
} else {
|
||||
managers.appUsersManager.reorderUsernames(usernames);
|
||||
}
|
||||
},
|
||||
setCursorTo: document.body,
|
||||
middleware: middleware,
|
||||
withDelay: true
|
||||
}
|
||||
});
|
||||
|
||||
section.content.append(list);
|
||||
|
|
|
@ -19,16 +19,19 @@ export default function handleHorizontalSwipe(options: SwipeHandlerHorizontalOpt
|
|||
...options,
|
||||
verifyTouchTarget: (e) => {
|
||||
return !findUpClassName(e.target, 'progress-line') &&
|
||||
!isSwipingBackSafari(e) &&
|
||||
!isSwipingBackSafari(e as any as TouchEvent) &&
|
||||
(options.verifyTouchTarget ? options.verifyTouchTarget(e) : true);
|
||||
},
|
||||
onSwipe: (xDiff, yDiff, e) => {
|
||||
xDiff *= -1;
|
||||
yDiff *= -1;
|
||||
|
||||
if(!cancelY && Math.abs(yDiff) > 20) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(Math.abs(xDiff) > Math.abs(yDiff)) {
|
||||
cancelEvent(e);
|
||||
cancelEvent(e as any as Event);
|
||||
cancelY = true;
|
||||
} else if(!cancelY && Math.abs(yDiff) > Math.abs(xDiff)/* || Math.abs(yDiff) > 20 */) {
|
||||
return true;
|
||||
|
|
|
@ -11,6 +11,9 @@ export default function handleTabSwipe(options: SwipeHandlerHorizontalOptions) {
|
|||
return handleHorizontalSwipe({
|
||||
...options,
|
||||
onSwipe: (xDiff, yDiff, e) => {
|
||||
xDiff *= -1;
|
||||
yDiff *= -1;
|
||||
|
||||
if(Math.abs(xDiff) > 50) {
|
||||
options.onSwipe(xDiff, yDiff, e);
|
||||
cancelContextMenuOpening();
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import {ScrollableBase} from '../../components/scrollable';
|
||||
import SwipeHandler from '../../components/swipeHandler';
|
||||
import IS_TOUCH_SUPPORTED from '../../environment/touchSupport';
|
||||
import rootScope from '../../lib/rootScope';
|
||||
import {Middleware} from '../middleware';
|
||||
import clamp from '../number/clamp';
|
||||
import safeAssign from '../object/safeAssign';
|
||||
import pause from '../schedulers/pause';
|
||||
import cancelEvent from './cancelEvent';
|
||||
import {attachClickEvent} from './clickEvent';
|
||||
import findUpAsChild from './findUpAsChild';
|
||||
import positionElementByIndex from './positionElementByIndex';
|
||||
import whichChild from './whichChild';
|
||||
|
||||
export default class Sortable {
|
||||
private element: HTMLElement;
|
||||
private elementRect: DOMRect;
|
||||
private containerRect: DOMRect;
|
||||
private scrollableRect: DOMRect;
|
||||
private minY: number;
|
||||
private maxY: number;
|
||||
private siblings: HTMLElement[];
|
||||
private swipeHandler: SwipeHandler;
|
||||
private startScrollPos: number;
|
||||
private addScrollPos: number;
|
||||
|
||||
private list: HTMLElement;
|
||||
private middleware: Middleware;
|
||||
private onSort: (prevIdx: number, newIdx: number) => void;
|
||||
private scrollable: ScrollableBase;
|
||||
|
||||
constructor(options: {
|
||||
list: HTMLElement,
|
||||
middleware: Middleware,
|
||||
onSort: Sortable['onSort'],
|
||||
scrollable?: Sortable['scrollable']
|
||||
}) {
|
||||
safeAssign(this, options);
|
||||
|
||||
this.swipeHandler = new SwipeHandler({
|
||||
element: this.list,
|
||||
onSwipe: this.onSwipe,
|
||||
verifyTouchTarget: this.verifyTouchTarget,
|
||||
onStart: this.onStart,
|
||||
onReset: this.onReset,
|
||||
setCursorTo: document.body,
|
||||
middleware: this.middleware,
|
||||
withDelay: true
|
||||
});
|
||||
}
|
||||
|
||||
private onSwipe = (xDiff: number, yDiff: number) => {
|
||||
yDiff = clamp(yDiff, this.minY, this.maxY);
|
||||
this.element.style.transform = `translateY(${yDiff}px)`;
|
||||
const count = Math.round(Math.abs(yDiff) / this.elementRect.height);
|
||||
const lastSiblings = this.siblings;
|
||||
this.siblings = [];
|
||||
const property = yDiff < 0 ? 'previousElementSibling' : 'nextElementSibling';
|
||||
let sibling = this.element[property] as HTMLElement;
|
||||
for(let i = 0; i < count; ++i) {
|
||||
if(this.getSortableTarget(sibling)) {
|
||||
this.siblings.push(sibling);
|
||||
sibling = sibling[property] as HTMLElement;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(lastSiblings || []).forEach((sibling) => {
|
||||
if(!this.siblings.includes(sibling)) {
|
||||
sibling.style.transform = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.siblings.forEach((sibling) => {
|
||||
const y = this.elementRect.height * (yDiff < 0 ? 1 : -1);
|
||||
sibling.style.transform = `translateY(${y}px)`;
|
||||
});
|
||||
|
||||
if(this.scrollableRect) {
|
||||
const diff = yDiff;
|
||||
const toEnd = diff > 0;
|
||||
const elementEndPos = toEnd ? this.elementRect.bottom : this.elementRect.top;
|
||||
const clientY = elementEndPos + diff - this.addScrollPos;
|
||||
// console.log(clientY, this.scrollableRect.top, elementEndPos, diff, this.addScrollPos, toEnd);
|
||||
let change = 2;
|
||||
if((clientY + (toEnd ? 0 : this.elementRect.height)) >= this.scrollableRect.bottom/* && diff < this.maxY */) {
|
||||
|
||||
} else if((clientY - (toEnd ? this.elementRect.height : 0)) <= this.scrollableRect.top/* && diff > this.minY */) {
|
||||
change *= -1;
|
||||
} else {
|
||||
change = undefined;
|
||||
}
|
||||
|
||||
if(change !== undefined) {
|
||||
this.scrollable.container[this.scrollable.scrollProperty] += change;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private verifyTouchTarget = (e: {target: EventTarget}) => {
|
||||
if(this.list.classList.contains('is-reordering')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.element = this.getSortableTarget(e.target as HTMLElement);
|
||||
return !!this.element/* && pause(150).then(() => true) */;
|
||||
};
|
||||
|
||||
private onScroll = () => {
|
||||
const scrollPos = this.scrollable.container[this.scrollable.scrollProperty];
|
||||
const diff = this.addScrollPos = scrollPos - this.startScrollPos;
|
||||
const isVertical = this.scrollable.scrollProperty === 'scrollTop';
|
||||
this.swipeHandler.add(isVertical ? 0 : diff, isVertical ? diff : 0);
|
||||
};
|
||||
|
||||
private onStart = () => {
|
||||
this.list.classList.add('is-reordering');
|
||||
this.element.classList.add('is-dragging', 'no-transition');
|
||||
this.swipeHandler.setCursor('grabbing');
|
||||
this.elementRect = this.element.getBoundingClientRect();
|
||||
this.containerRect = this.list.getBoundingClientRect();
|
||||
|
||||
this.minY = this.containerRect.top - this.elementRect.top;
|
||||
this.maxY = this.containerRect.bottom - this.elementRect.bottom;
|
||||
this.addScrollPos = 0;
|
||||
|
||||
if(this.scrollable) {
|
||||
this.startScrollPos = this.scrollable.container[this.scrollable.scrollProperty];
|
||||
this.scrollableRect = this.scrollable.container.getBoundingClientRect();
|
||||
this.scrollable.container.addEventListener('scroll', this.onScroll);
|
||||
}
|
||||
};
|
||||
|
||||
private onReset = async() => {
|
||||
const length = this.siblings.length;
|
||||
const move = length && length * (this.siblings[0].previousElementSibling === this.element ? 1 : -1);
|
||||
const idx = whichChild(this.element);
|
||||
const newIdx = idx + move;
|
||||
|
||||
this.element.classList.remove('no-transition');
|
||||
this.element.style.transform = move ? `translateY(${move * this.elementRect.height}px)` : '';
|
||||
this.swipeHandler.setCursor('');
|
||||
|
||||
if(this.scrollable) {
|
||||
this.scrollable.container.removeEventListener('scroll', this.onScroll);
|
||||
}
|
||||
|
||||
if(!IS_TOUCH_SUPPORTED) {
|
||||
attachClickEvent(document.body, cancelEvent, {capture: true, once: true});
|
||||
}
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
await pause(250);
|
||||
}
|
||||
|
||||
this.list.classList.remove('is-reordering');
|
||||
this.element.classList.remove('is-dragging');
|
||||
positionElementByIndex(this.element, this.list, newIdx, idx);
|
||||
[this.element, ...this.siblings].forEach((element) => {
|
||||
element.style.transform = '';
|
||||
});
|
||||
|
||||
this.element =
|
||||
this.siblings =
|
||||
this.elementRect =
|
||||
this.containerRect =
|
||||
this.minY =
|
||||
this.maxY =
|
||||
this.startScrollPos =
|
||||
this.addScrollPos =
|
||||
undefined;
|
||||
|
||||
// cancelClick = true;
|
||||
|
||||
if(!move) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onSort(idx, newIdx);
|
||||
};
|
||||
|
||||
private getSortableTarget(target: HTMLElement) {
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
let child = findUpAsChild(target as HTMLElement, this.list);
|
||||
if(child && child.classList.contains('cant-sort')) {
|
||||
child = undefined;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
|
@ -1672,7 +1672,7 @@ export class AppDialogsManager {
|
|||
element: this.folders.container,
|
||||
onSwipe: (xDiff) => {
|
||||
const prevId = selectTab.prevId();
|
||||
selectTab(xDiff > 0 ? prevId + 1 : prevId - 1);
|
||||
selectTab(xDiff < 0 ? prevId + 1 : prevId - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -380,6 +380,17 @@ export default class FiltersStorage extends AppManager {
|
|||
});
|
||||
}
|
||||
|
||||
public updateDialogFiltersOrder(order: number[]) {
|
||||
return this.apiManager.invokeApi('messages.updateDialogFiltersOrder', {
|
||||
order
|
||||
}).then(() => {
|
||||
this.onUpdateDialogFilterOrder({
|
||||
_: 'updateDialogFilterOrder',
|
||||
order
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getOutputDialogFilter(filter: MyDialogFilter) {
|
||||
const c = copy(filter);
|
||||
/* convertment.forEach(([from, to]) => {
|
||||
|
|
Loading…
Reference in New Issue