Sorting folders

This commit is contained in:
Eduard Kuzmenko 2023-01-13 18:50:48 +04:00
parent a88043f966
commit beabcb2a23
14 changed files with 351 additions and 165 deletions

View File

@ -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: () => {

View File

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

View File

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

View File

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

View File

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

View File

@ -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) => {

View File

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

View File

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

View File

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

View File

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

View File

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

201
src/helpers/dom/sortable.ts Normal file
View File

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

View File

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

View File

@ -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]) => {