Swipe animation for profile avatars
Search refactor Fix dialogs slicing
This commit is contained in:
parent
cd37d44db1
commit
3119e7788a
@ -194,39 +194,43 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
this.wholeDiv.addEventListener('click', this.onClick);
|
||||
|
||||
if(isTouchSupported) {
|
||||
const swipeHandler = new SwipeHandler(this.wholeDiv, (xDiff, yDiff) => {
|
||||
if(VideoPlayer.isFullScreen()) {
|
||||
return;
|
||||
}
|
||||
//console.log(xDiff, yDiff);
|
||||
const swipeHandler = new SwipeHandler({
|
||||
element: this.wholeDiv,
|
||||
onSwipe: (xDiff, yDiff) => {
|
||||
if(VideoPlayer.isFullScreen()) {
|
||||
return;
|
||||
}
|
||||
//console.log(xDiff, yDiff);
|
||||
|
||||
const percents = Math.abs(xDiff) / appPhotosManager.windowW;
|
||||
if(percents > .2 || xDiff > 125) {
|
||||
//console.log('will swipe', xDiff);
|
||||
const percents = Math.abs(xDiff) / appPhotosManager.windowW;
|
||||
if(percents > .2 || xDiff > 125) {
|
||||
//console.log('will swipe', xDiff);
|
||||
|
||||
if(xDiff < 0) {
|
||||
this.buttons.prev.click();
|
||||
} else {
|
||||
this.buttons.next.click();
|
||||
if(xDiff < 0) {
|
||||
this.buttons.prev.click();
|
||||
} else {
|
||||
this.buttons.next.click();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const percentsY = Math.abs(yDiff) / appPhotosManager.windowH;
|
||||
if(percentsY > .2 || yDiff > 125) {
|
||||
this.buttons.close.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
verifyTouchTarget: (evt) => {
|
||||
// * Fix for seek input
|
||||
if((evt.target as HTMLElement).tagName === 'INPUT' || findUpClassName(evt.target, 'media-viewer-caption')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const percentsY = Math.abs(yDiff) / appPhotosManager.windowH;
|
||||
if(percentsY > .2 || yDiff > 125) {
|
||||
this.buttons.close.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, (evt) => {
|
||||
// * Fix for seek input
|
||||
if((evt.target as HTMLElement).tagName === 'INPUT' || findUpClassName(evt.target, 'media-viewer-caption')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
import { formatDateAccordingToToday, months } from "../helpers/date";
|
||||
import { positionElementByIndex } from "../helpers/dom";
|
||||
import { copy, getObjectKeysAndSort } from "../helpers/object";
|
||||
import { copy, getObjectKeysAndSort, safeAssign } from "../helpers/object";
|
||||
import { escapeRegExp, limitSymbols } from "../helpers/string";
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
import appDialogsManager from "../lib/appManagers/appDialogsManager";
|
||||
@ -25,11 +25,11 @@ import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHe
|
||||
import { isSafari } from "../helpers/userAgent";
|
||||
import { LangPackKey, i18n } from "../lib/langPack";
|
||||
import findUpClassName from "../helpers/dom/findUpClassName";
|
||||
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
|
||||
import { getMiddleware } from "../helpers/middleware";
|
||||
|
||||
//const testScroll = false;
|
||||
|
||||
export type SearchSuperType = MyInputMessagesFilter/* | 'chats' */;
|
||||
export type SearchSuperType = MyInputMessagesFilter/* | 'members' */;
|
||||
export type SearchSuperContext = {
|
||||
peerId: number,
|
||||
inputFilter: MyInputMessagesFilter,
|
||||
@ -43,10 +43,20 @@ export type SearchSuperContext = {
|
||||
maxDate?: number
|
||||
};
|
||||
|
||||
export type SearchSuperMediaType = 'members' | 'media' | 'files' | 'links' | 'music' | 'chats' | 'voice';
|
||||
export type SearchSuperMediaTab = {
|
||||
inputFilter: SearchSuperType,
|
||||
name: LangPackKey,
|
||||
type: SearchSuperMediaType,
|
||||
contentTab?: HTMLElement,
|
||||
menuTab?: HTMLElement,
|
||||
scroll?: {scrollTop: number, scrollHeight: number}
|
||||
};
|
||||
|
||||
export default class AppSearchSuper {
|
||||
public tabs: {[t in SearchSuperType]: HTMLDivElement} = {} as any;
|
||||
|
||||
public type: SearchSuperType;
|
||||
public mediaTab: SearchSuperMediaTab;
|
||||
public tabSelected: HTMLElement;
|
||||
|
||||
public container: HTMLElement;
|
||||
@ -56,7 +66,7 @@ export default class AppSearchSuper {
|
||||
private prevTabId = -1;
|
||||
|
||||
private lazyLoadQueue = new LazyLoadQueue();
|
||||
private cleanupObj = {cleaned: false};
|
||||
public middleware = getMiddleware();
|
||||
|
||||
public historyStorage: Partial<{[type in SearchSuperType]: {mid: number, peerId: number}[]}> = {};
|
||||
public usedFromHistory: Partial<{[type in SearchSuperType]: number}> = {};
|
||||
@ -84,9 +94,19 @@ export default class AppSearchSuper {
|
||||
|
||||
private searchGroupMedia: SearchGroup;
|
||||
|
||||
public goingHard: Partial<{[type in MyInputMessagesFilter]: {scrollTop: number, scrollHeight: number}}> = {};
|
||||
public mediaTabsMap: Map<SearchSuperMediaType, SearchSuperMediaTab> = new Map();
|
||||
|
||||
// * arguments
|
||||
public mediaTabs: SearchSuperMediaTab[];
|
||||
public scrollable: Scrollable;
|
||||
public searchGroups?: {[group in SearchGroupType]: SearchGroup};
|
||||
public asChatList? = false;
|
||||
public groupByMonth? = true;
|
||||
public hideEmptyTabs? = true;
|
||||
|
||||
constructor(options: Pick<AppSearchSuper, 'mediaTabs' | 'scrollable' | 'searchGroups' | 'asChatList' | 'groupByMonth' | 'hideEmptyTabs'>) {
|
||||
safeAssign(this, options);
|
||||
|
||||
constructor(public types: {inputFilter: SearchSuperType, name: LangPackKey, type: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false, public groupByMonth = true) {
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add('search-super');
|
||||
|
||||
@ -101,13 +121,13 @@ export default class AppSearchSuper {
|
||||
|
||||
navScrollable.container.append(nav);
|
||||
|
||||
for(const type of types) {
|
||||
for(const mediaTab of this.mediaTabs) {
|
||||
const menuTab = document.createElement('div');
|
||||
menuTab.classList.add('menu-horizontal-div-item');
|
||||
const span = document.createElement('span');
|
||||
const i = document.createElement('i');
|
||||
|
||||
span.append(i18n(type.name));
|
||||
span.append(i18n(mediaTab.name));
|
||||
span.append(i);
|
||||
|
||||
menuTab.append(span);
|
||||
@ -115,38 +135,29 @@ export default class AppSearchSuper {
|
||||
ripple(menuTab);
|
||||
|
||||
this.tabsMenu.append(menuTab);
|
||||
|
||||
this.mediaTabsMap.set(mediaTab.type, mediaTab);
|
||||
|
||||
mediaTab.menuTab = menuTab;
|
||||
}
|
||||
|
||||
this.tabsContainer = document.createElement('div');
|
||||
this.tabsContainer.classList.add('search-super-tabs-container', 'tabs-container');
|
||||
|
||||
for(const type of types) {
|
||||
for(const mediaTab of this.mediaTabs) {
|
||||
const container = document.createElement('div');
|
||||
container.classList.add('search-super-container-' + type.type/* , 'scrollable', 'scrollable-y' */);
|
||||
container.classList.add('search-super-container-' + mediaTab.type);
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.classList.add('search-super-content-' + type.type/* , 'scrollable', 'scrollable-y' */);
|
||||
|
||||
//content.style.overflowY = 'hidden';
|
||||
/* container.style.overflow = 'visible';
|
||||
const v = 236;
|
||||
content.style.top = (v * -1) + 'px';
|
||||
content.style.paddingTop = v + 'px';
|
||||
content.style.height = `calc(100% + ${v}px)`;
|
||||
content.style.maxHeight = `calc(100% + ${v}px)`;
|
||||
content.addEventListener('scroll', (e) => {
|
||||
const scrollTop = content.scrollTop;
|
||||
if(scrollTop <= v) {
|
||||
//this.scrollable.scrollTop = scrollTop;
|
||||
(this.container.previousElementSibling as HTMLElement).style.transform = `translateY(-${scrollTop}px)`;
|
||||
}
|
||||
}); */
|
||||
content.classList.add('search-super-content-' + mediaTab.type);
|
||||
|
||||
container.append(content);
|
||||
|
||||
this.tabsContainer.append(container);
|
||||
|
||||
this.tabs[type.inputFilter] = content;
|
||||
this.tabs[mediaTab.inputFilter] = content;
|
||||
|
||||
mediaTab.contentTab = content;
|
||||
}
|
||||
|
||||
this.container.append(navScrollableContainer, this.tabsContainer);
|
||||
@ -169,24 +180,24 @@ export default class AppSearchSuper {
|
||||
if(this.prevTabId !== -1) {
|
||||
this.onTransitionStart();
|
||||
}
|
||||
|
||||
this.mediaTab.scroll = {scrollTop: this.scrollable.scrollTop, scrollHeight: this.scrollable.scrollHeight};
|
||||
|
||||
this.goingHard[this.type] = {scrollTop: this.scrollable.scrollTop, scrollHeight: this.scrollable.scrollHeight};
|
||||
|
||||
const newType = this.types[id].inputFilter;
|
||||
const newMediaTab = this.mediaTabs[id];
|
||||
this.tabSelected = tabContent.firstElementChild as HTMLDivElement;
|
||||
|
||||
if(this.goingHard[newType] === undefined) {
|
||||
if(newMediaTab.scroll === undefined) {
|
||||
const rect = this.container.getBoundingClientRect();
|
||||
const rect2 = this.container.parentElement.getBoundingClientRect();
|
||||
const diff = rect.y - rect2.y;
|
||||
|
||||
if(this.scrollable.scrollTop > diff) {
|
||||
this.goingHard[newType] = {scrollTop: diff, scrollHeight: 0};
|
||||
newMediaTab.scroll = {scrollTop: diff, scrollHeight: 0};
|
||||
}
|
||||
}
|
||||
|
||||
if(this.goingHard[newType]) {
|
||||
const diff = this.goingHard[this.type].scrollTop - this.goingHard[newType].scrollTop;
|
||||
if(newMediaTab.scroll) {
|
||||
const diff = this.mediaTab.scroll.scrollTop - newMediaTab.scroll.scrollTop;
|
||||
//console.log('what you gonna do', this.goingHard, diff);
|
||||
|
||||
if(diff/* && diff < 0 */) {
|
||||
@ -194,7 +205,7 @@ export default class AppSearchSuper {
|
||||
}
|
||||
}
|
||||
|
||||
this.type = newType;
|
||||
this.mediaTab = newMediaTab;
|
||||
|
||||
/* if(this.prevTabId !== -1 && nav.offsetTop) {
|
||||
this.scrollable.scrollTop -= nav.offsetTop;
|
||||
@ -213,9 +224,9 @@ export default class AppSearchSuper {
|
||||
this.scrollable.onScroll();
|
||||
|
||||
//console.log('what y', this.tabSelected.style.transform);
|
||||
if(this.goingHard[this.type] !== undefined) {
|
||||
if(this.mediaTab.scroll !== undefined) {
|
||||
this.tabSelected.style.transform = '';
|
||||
this.scrollable.scrollTop = this.goingHard[this.type].scrollTop;
|
||||
this.scrollable.scrollTop = this.mediaTab.scroll.scrollTop;
|
||||
}
|
||||
|
||||
this.onTransitionEnd();
|
||||
@ -241,11 +252,11 @@ export default class AppSearchSuper {
|
||||
|
||||
const message = appMessagesManager.getMessageByPeer(peerId, mid);
|
||||
new AppMediaViewer()
|
||||
.setSearchContext(this.copySearchContext(this.type))
|
||||
.setSearchContext(this.copySearchContext(this.mediaTab.inputFilter))
|
||||
.openMedia(message, target, 0, false, targets.slice(0, idx), targets.slice(idx + 1));
|
||||
});
|
||||
|
||||
this.type = this.types[0].inputFilter;
|
||||
this.mediaTab = this.mediaTabs[0];
|
||||
|
||||
useHeavyAnimationCheck(() => {
|
||||
this.lazyLoadQueue.lock();
|
||||
@ -381,7 +392,7 @@ export default class AppSearchSuper {
|
||||
const elemsToAppend: {element: HTMLElement, message: any}[] = [];
|
||||
const sharedMediaDiv: HTMLElement = this.tabs[type];
|
||||
const promises: Promise<any>[] = [];
|
||||
const middleware = this.getMiddleware();
|
||||
const middleware = this.middleware.get();
|
||||
|
||||
await getHeavyAnimationPromise();
|
||||
|
||||
@ -538,23 +549,22 @@ export default class AppSearchSuper {
|
||||
|
||||
//this.log('wrapping webpage', webpage);
|
||||
|
||||
previewDiv.innerHTML = RichTextProcessor.getAbbreviation(webpage.title || webpage.display_url || webpage.description || webpage.url, true);
|
||||
previewDiv.classList.add('empty');
|
||||
if(webpage.photo) {
|
||||
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 60, 60))
|
||||
.then(() => {
|
||||
if(!middleware()) {
|
||||
//this.log.warn('peer changed');
|
||||
return;
|
||||
}
|
||||
|
||||
previewDiv.classList.remove('empty');
|
||||
|
||||
previewDiv.innerText = '';
|
||||
renderImageFromUrl(previewDiv, webpage.photo.url);
|
||||
const res = wrapPhoto({
|
||||
container: previewDiv,
|
||||
message: null,
|
||||
photo: webpage.photo,
|
||||
boxWidth: 0,
|
||||
boxHeight: 0,
|
||||
withoutPreloader: true,
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
middleware,
|
||||
size: appPhotosManager.choosePhotoSize(webpage.photo, 60, 60, false),
|
||||
loadPromises: promises
|
||||
});
|
||||
|
||||
this.lazyLoadQueue.push({div: previewDiv, load});
|
||||
} else {
|
||||
previewDiv.classList.add('empty');
|
||||
previewDiv.innerHTML = RichTextProcessor.getAbbreviation(webpage.title || webpage.display_url || webpage.description || webpage.url, true);
|
||||
}
|
||||
|
||||
let title = webpage.rTitle || '';
|
||||
@ -624,16 +634,16 @@ export default class AppSearchSuper {
|
||||
//}
|
||||
}
|
||||
|
||||
private afterPerforming(length: number, tab: HTMLElement) {
|
||||
if(tab) {
|
||||
const parent = tab.parentElement;
|
||||
private afterPerforming(length: number, contentTab: HTMLElement) {
|
||||
if(contentTab) {
|
||||
const parent = contentTab.parentElement;
|
||||
Array.from(parent.children).slice(1).forEach(child => {
|
||||
child.remove();
|
||||
});
|
||||
|
||||
//this.contentContainer.classList.add('loaded');
|
||||
|
||||
if(!length && !tab.childElementCount) {
|
||||
if(!length && !contentTab.childElementCount) {
|
||||
const div = document.createElement('div');
|
||||
div.innerText = 'Nothing interesting here yet...';
|
||||
div.classList.add('position-center', 'text-center', 'content-empty', 'no-select');
|
||||
@ -645,7 +655,7 @@ export default class AppSearchSuper {
|
||||
|
||||
private loadChats() {
|
||||
const renderedPeerIds: Set<number> = new Set();
|
||||
const middleware = this.getMiddleware();
|
||||
const middleware = this.middleware.get();
|
||||
|
||||
for(let i in this.searchGroups) {
|
||||
const group = this.searchGroups[i as SearchGroupType];
|
||||
@ -807,6 +817,135 @@ export default class AppSearchSuper {
|
||||
]);
|
||||
} else return Promise.resolve();
|
||||
}
|
||||
|
||||
private loadType(mediaTab: SearchSuperMediaTab, justLoad: boolean, loadCount: number, middleware: () => boolean) {
|
||||
const type = mediaTab.inputFilter;
|
||||
|
||||
if(this.loadPromises[type]) {
|
||||
return this.loadPromises[type];
|
||||
}
|
||||
|
||||
const history = this.historyStorage[type] ?? (this.historyStorage[type] = []);
|
||||
|
||||
if(type === 'inputMessagesFilterEmpty' && !history.length) {
|
||||
if(!this.loadedChats) {
|
||||
this.loadChats();
|
||||
this.loadedChats = true;
|
||||
}
|
||||
|
||||
if(!this.searchContext.query.trim() && !this.searchContext.peerId && !this.searchContext.minDate) {
|
||||
this.loaded[type] = true;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
const logStr = 'load [' + type + ']: ';
|
||||
|
||||
// render from cache
|
||||
if(history.length && this.usedFromHistory[type] < history.length && !justLoad) {
|
||||
let messages: any[] = [];
|
||||
let used = Math.max(0, this.usedFromHistory[type]);
|
||||
let slicedLength = 0;
|
||||
|
||||
do {
|
||||
let ids = history.slice(used, used + loadCount);
|
||||
//this.log(logStr + 'will render from cache', used, history, ids, loadCount);
|
||||
used += ids.length;
|
||||
slicedLength += ids.length;
|
||||
|
||||
messages.push(...this.filterMessagesByType(ids.map(m => appMessagesManager.getMessageByPeer(m.peerId, m.mid)), type));
|
||||
} while(slicedLength < loadCount && used < history.length);
|
||||
|
||||
// если перебор
|
||||
/* if(slicedLength > loadCount) {
|
||||
let diff = messages.length - loadCount;
|
||||
messages = messages.slice(0, messages.length - diff);
|
||||
used -= diff;
|
||||
} */
|
||||
|
||||
this.usedFromHistory[type] = used;
|
||||
//if(messages.length) {
|
||||
return this.performSearchResult(messages, type).finally(() => {
|
||||
setTimeout(() => {
|
||||
this.scrollable.checkForTriggers();
|
||||
}, 0);
|
||||
});
|
||||
//}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let maxId = history.length ? history[history.length - 1].mid : 0;
|
||||
|
||||
//this.log(logStr + 'search house of glass pre', type, maxId);
|
||||
|
||||
//let loadCount = history.length ? 50 : 15;
|
||||
return this.loadPromises[type] = appMessagesManager.getSearch({
|
||||
peerId: this.searchContext.peerId,
|
||||
query: this.searchContext.query,
|
||||
inputFilter: {_: type},
|
||||
maxId,
|
||||
limit: loadCount,
|
||||
nextRate: this.nextRates[type] ?? (this.nextRates[type] = 0),
|
||||
threadId: this.searchContext.threadId,
|
||||
folderId: this.searchContext.folderId,
|
||||
minDate: this.searchContext.minDate,
|
||||
maxDate: this.searchContext.maxDate
|
||||
}).then(value => {
|
||||
history.push(...value.history.map(m => ({mid: m.mid, peerId: m.peerId})));
|
||||
|
||||
this.log(logStr + 'search house of glass', type, value);
|
||||
|
||||
if(!middleware()) {
|
||||
//this.log.warn('peer changed');
|
||||
return;
|
||||
}
|
||||
|
||||
// ! Фикс случая, когда не загружаются документы при открытой панели разработчиков (происходит из-за того, что не совпадают критерии отбора документов в getSearch)
|
||||
if(value.history.length < loadCount) {
|
||||
//if((value.count || history.length === value.count) && history.length >= value.count) {
|
||||
//this.log(logStr + 'loaded all media', value, loadCount);
|
||||
this.loaded[type] = true;
|
||||
}
|
||||
|
||||
this.nextRates[type] = value.next_rate;
|
||||
|
||||
if(justLoad) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this.usedFromHistory[type] = history.length;
|
||||
|
||||
if(!this.loaded[type]) {
|
||||
(this.loadPromises[type] || Promise.resolve()).then(() => {
|
||||
setTimeout(() => {
|
||||
if(!middleware()) return;
|
||||
//this.log('will preload more');
|
||||
if(this.mediaTab === mediaTab) {
|
||||
const promise = this.load(true, true);
|
||||
if(promise) {
|
||||
promise.then(() => {
|
||||
if(!middleware()) return;
|
||||
//this.log('preloaded more');
|
||||
setTimeout(() => {
|
||||
this.scrollable.checkForTriggers();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
//if(value.history.length) {
|
||||
return this.performSearchResult(this.filterMessagesByType(value.history, type), type);
|
||||
//}
|
||||
}).catch(err => {
|
||||
this.log.error('load error:', err);
|
||||
}).finally(() => {
|
||||
this.loadPromises[type] = null;
|
||||
});
|
||||
}
|
||||
|
||||
public load(single = false, justLoad = false) {
|
||||
// if(testScroll/* || 1 === 1 */) {
|
||||
@ -818,141 +957,21 @@ export default class AppSearchSuper {
|
||||
const peerId = this.searchContext.peerId;
|
||||
this.log('load', single, peerId, this.loadPromises);
|
||||
|
||||
let typesToLoad = single ? [this.type] : this.types.filter(t => t.inputFilter !== this.type).map(t => t.inputFilter);
|
||||
typesToLoad = typesToLoad.filter(type => !this.loaded[type]
|
||||
|| (this.historyStorage[type] && this.usedFromHistory[type] < this.historyStorage[type].length));
|
||||
let toLoad = single ? [this.mediaTab] : this.mediaTabs.filter(t => t !== this.mediaTab);
|
||||
toLoad = toLoad.filter(mediaTab => {
|
||||
const inputFilter = mediaTab.inputFilter;
|
||||
return !this.loaded[inputFilter] || (this.historyStorage[inputFilter] && this.usedFromHistory[inputFilter] < this.historyStorage[inputFilter].length);
|
||||
});
|
||||
|
||||
if(!typesToLoad.length) return;
|
||||
if(!toLoad.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadCount = justLoad ? 50 : Math.round((appPhotosManager.windowH / 130 | 0) * 3 * 1.25); // that's good for all types
|
||||
|
||||
const historyStorage = this.historyStorage ?? (this.historyStorage = {});
|
||||
const middleware = this.middleware.get();
|
||||
|
||||
const middleware = this.getMiddleware();
|
||||
|
||||
const promises: Promise<any>[] = typesToLoad.map(type => {
|
||||
if(this.loadPromises[type]) return this.loadPromises[type];
|
||||
|
||||
const history = historyStorage[type] ?? (historyStorage[type] = []);
|
||||
|
||||
if(type === 'inputMessagesFilterEmpty' && !history.length) {
|
||||
if(!this.loadedChats) {
|
||||
this.loadChats();
|
||||
this.loadedChats = true;
|
||||
}
|
||||
|
||||
if(!this.searchContext.query.trim() && !this.searchContext.peerId && !this.searchContext.minDate) {
|
||||
this.loaded[type] = true;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
const logStr = 'load [' + type + ']: ';
|
||||
|
||||
// render from cache
|
||||
if(history.length && this.usedFromHistory[type] < history.length && !justLoad) {
|
||||
let messages: any[] = [];
|
||||
let used = Math.max(0, this.usedFromHistory[type]);
|
||||
let slicedLength = 0;
|
||||
|
||||
do {
|
||||
let ids = history.slice(used, used + loadCount);
|
||||
//this.log(logStr + 'will render from cache', used, history, ids, loadCount);
|
||||
used += ids.length;
|
||||
slicedLength += ids.length;
|
||||
|
||||
messages.push(...this.filterMessagesByType(ids.map(m => appMessagesManager.getMessageByPeer(m.peerId, m.mid)), type));
|
||||
} while(slicedLength < loadCount && used < history.length);
|
||||
|
||||
// если перебор
|
||||
/* if(slicedLength > loadCount) {
|
||||
let diff = messages.length - loadCount;
|
||||
messages = messages.slice(0, messages.length - diff);
|
||||
used -= diff;
|
||||
} */
|
||||
|
||||
this.usedFromHistory[type] = used;
|
||||
//if(messages.length) {
|
||||
return this.performSearchResult(messages, type).finally(() => {
|
||||
setTimeout(() => {
|
||||
this.scrollable.checkForTriggers();
|
||||
}, 0);
|
||||
});
|
||||
//}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let maxId = history.length ? history[history.length - 1].mid : 0;
|
||||
|
||||
//this.log(logStr + 'search house of glass pre', type, maxId);
|
||||
|
||||
//let loadCount = history.length ? 50 : 15;
|
||||
return this.loadPromises[type] = appMessagesManager.getSearch({
|
||||
peerId,
|
||||
query: this.searchContext.query,
|
||||
inputFilter: {_: type},
|
||||
maxId,
|
||||
limit: loadCount,
|
||||
nextRate: this.nextRates[type] ?? (this.nextRates[type] = 0),
|
||||
threadId: this.searchContext.threadId,
|
||||
folderId: this.searchContext.folderId,
|
||||
minDate: this.searchContext.minDate,
|
||||
maxDate: this.searchContext.maxDate
|
||||
}).then(value => {
|
||||
history.push(...value.history.map(m => ({mid: m.mid, peerId: m.peerId})));
|
||||
|
||||
this.log(logStr + 'search house of glass', type, value);
|
||||
|
||||
if(!middleware()) {
|
||||
//this.log.warn('peer changed');
|
||||
return;
|
||||
}
|
||||
|
||||
// ! Фикс случая, когда не загружаются документы при открытой панели разработчиков (происходит из-за того, что не совпадают критерии отбора документов в getSearch)
|
||||
if(value.history.length < loadCount) {
|
||||
//if((value.count || history.length === value.count) && history.length >= value.count) {
|
||||
//this.log(logStr + 'loaded all media', value, loadCount);
|
||||
this.loaded[type] = true;
|
||||
}
|
||||
|
||||
this.nextRates[type] = value.next_rate;
|
||||
|
||||
if(justLoad) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this.usedFromHistory[type] = history.length;
|
||||
|
||||
if(!this.loaded[type]) {
|
||||
(this.loadPromises[type] || Promise.resolve()).then(() => {
|
||||
setTimeout(() => {
|
||||
if(!middleware()) return;
|
||||
//this.log('will preload more');
|
||||
if(this.type === type) {
|
||||
const promise = this.load(true, true);
|
||||
if(promise) {
|
||||
promise.then(() => {
|
||||
if(!middleware()) return;
|
||||
//this.log('preloaded more');
|
||||
setTimeout(() => {
|
||||
this.scrollable.checkForTriggers();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
//if(value.history.length) {
|
||||
return this.performSearchResult(this.filterMessagesByType(value.history, type), type);
|
||||
//}
|
||||
}).catch(err => {
|
||||
this.log.error('load error:', err);
|
||||
}).finally(() => {
|
||||
this.loadPromises[type] = null;
|
||||
});
|
||||
const promises: Promise<any>[] = toLoad.map(mediaTab => {
|
||||
return this.loadType(mediaTab, justLoad, loadCount, middleware)
|
||||
});
|
||||
|
||||
return Promise.all(promises).catch(err => {
|
||||
@ -1006,13 +1025,18 @@ export default class AppSearchSuper {
|
||||
|
||||
this.lazyLoadQueue.clear();
|
||||
|
||||
this.types.forEach(type => {
|
||||
this.usedFromHistory[type.inputFilter] = -1;
|
||||
this.mediaTabs.forEach(mediaTab => {
|
||||
this.usedFromHistory[mediaTab.inputFilter] = -1;
|
||||
});
|
||||
|
||||
this.cleanupObj.cleaned = true;
|
||||
this.cleanupObj = {cleaned: false};
|
||||
this.goingHard = {};
|
||||
this.middleware.clean();
|
||||
this.cleanScrollPositions();
|
||||
}
|
||||
|
||||
public cleanScrollPositions() {
|
||||
this.mediaTabs.forEach(mediaTab => {
|
||||
mediaTab.scroll = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
public cleanupHTML() {
|
||||
@ -1023,15 +1047,15 @@ export default class AppSearchSuper {
|
||||
this.urlsToRevoke.length = 0;
|
||||
}
|
||||
|
||||
(Object.keys(this.tabs) as SearchSuperType[]).forEach(type => {
|
||||
this.tabs[type].innerHTML = '';
|
||||
this.mediaTabs.forEach((tab) => {
|
||||
tab.contentTab.innerHTML = '';
|
||||
|
||||
if(type === 'inputMessagesFilterEmpty') {
|
||||
if(tab.type === 'chats') {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.historyStorage || !this.historyStorage[type]) {
|
||||
const parent = this.tabs[type].parentElement;
|
||||
if(!this.historyStorage[tab.inputFilter]) {
|
||||
const parent = tab.contentTab.parentElement;
|
||||
//if(!testScroll) {
|
||||
if(!parent.querySelector('.preloader')) {
|
||||
putPreloader(parent, true);
|
||||
@ -1044,7 +1068,7 @@ export default class AppSearchSuper {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.monthContainers = {};
|
||||
this.searchGroupMedia.clear();
|
||||
this.scrollable.scrollTop = 0;
|
||||
@ -1061,14 +1085,6 @@ export default class AppSearchSuper {
|
||||
} */
|
||||
}
|
||||
|
||||
// * will change .cleaned in cleanup() and new instance will be created
|
||||
public getMiddleware() {
|
||||
const cleanupObj = this.cleanupObj;
|
||||
return () => {
|
||||
return !cleanupObj.cleaned;
|
||||
};
|
||||
}
|
||||
|
||||
private copySearchContext(newInputFilter: MyInputMessagesFilter) {
|
||||
const context = copy(this.searchContext);
|
||||
context.inputFilter = newInputFilter;
|
||||
@ -1088,7 +1104,7 @@ export default class AppSearchSuper {
|
||||
this.searchContext = {
|
||||
peerId: peerId || 0,
|
||||
query: query || '',
|
||||
inputFilter: this.type,
|
||||
inputFilter: this.mediaTab.inputFilter,
|
||||
threadId,
|
||||
folderId,
|
||||
minDate,
|
||||
|
@ -829,7 +829,7 @@ export default class ChatBubbles {
|
||||
//appSidebarRight.forwardTab.open([mid]);
|
||||
return;
|
||||
} else if(target.classList.contains('peer-title') || target.classList.contains('name')) {
|
||||
target = findUpClassName(target, 'name');
|
||||
target = findUpClassName(target, 'name') || target;
|
||||
const peerId = +target.dataset.peerId;
|
||||
const savedFrom = target.dataset.savedFrom;
|
||||
if(savedFrom) {
|
||||
|
@ -193,31 +193,37 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
recent: new SearchGroup('Recent', 'contacts', true, 'search-group-recent', true, true, close)
|
||||
};
|
||||
|
||||
const searchSuper = this.searchSuper = new AppSearchSuper([{
|
||||
inputFilter: 'inputMessagesFilterEmpty',
|
||||
name: 'FilterChats',
|
||||
type: 'chats'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterPhotoVideo',
|
||||
name: 'SharedMediaTab2',
|
||||
type: 'media'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterUrl',
|
||||
name: 'SharedLinksTab2',
|
||||
type: 'links'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterDocument',
|
||||
name: 'SharedFilesTab2',
|
||||
type: 'files'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterMusic',
|
||||
name: 'SharedMusicTab2',
|
||||
type: 'music'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterVoice',
|
||||
name: 'SharedVoiceTab2',
|
||||
type: 'voice'
|
||||
}], scrollable, this.searchGroups, true);
|
||||
const searchSuper = this.searchSuper = new AppSearchSuper({
|
||||
mediaTabs: [{
|
||||
inputFilter: 'inputMessagesFilterEmpty',
|
||||
name: 'FilterChats',
|
||||
type: 'chats'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterPhotoVideo',
|
||||
name: 'SharedMediaTab2',
|
||||
type: 'media'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterUrl',
|
||||
name: 'SharedLinksTab2',
|
||||
type: 'links'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterDocument',
|
||||
name: 'SharedFilesTab2',
|
||||
type: 'files'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterMusic',
|
||||
name: 'SharedMusicTab2',
|
||||
type: 'music'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterVoice',
|
||||
name: 'SharedVoiceTab2',
|
||||
type: 'voice'
|
||||
}],
|
||||
scrollable,
|
||||
searchGroups: this.searchGroups,
|
||||
asChatList: true,
|
||||
hideEmptyTabs: false
|
||||
});
|
||||
|
||||
searchContainer.prepend(searchSuper.nav.parentElement.parentElement);
|
||||
scrollable.container.append(searchSuper.container);
|
||||
@ -347,7 +353,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
}
|
||||
|
||||
if(!selectedPeerId && value.trim()) {
|
||||
const middleware = searchSuper.getMiddleware();
|
||||
const middleware = searchSuper.middleware.get();
|
||||
Promise.all([
|
||||
appMessagesManager.getConversationsAll(value).then(dialogs => dialogs.map(d => d.peerId)),
|
||||
appUsersManager.getContacts(value, true)
|
||||
|
@ -10,7 +10,7 @@ import AppSearchSuper, { SearchSuperType } from "../../appSearchSuper.";
|
||||
import AvatarElement from "../../avatar";
|
||||
import SidebarSlider, { SliderSuperTab } from "../../slider";
|
||||
import CheckboxField from "../../checkboxField";
|
||||
import { attachClickEvent, replaceContent } from "../../../helpers/dom";
|
||||
import { attachClickEvent, replaceContent, whichChild } from "../../../helpers/dom";
|
||||
import appSidebarRight from "..";
|
||||
import { TransitionSlider } from "../../transition";
|
||||
import appNotificationsManager from "../../../lib/appManagers/appNotificationsManager";
|
||||
@ -32,6 +32,7 @@ import { safeAssign } from "../../../helpers/object";
|
||||
import { forEachReverse } from "../../../helpers/array";
|
||||
import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
|
||||
import renderImageFromUrl from "../../../helpers/dom/renderImageFromUrl";
|
||||
import SwipeHandler from "../../swipeHandler";
|
||||
|
||||
let setText = (text: string, row: Row) => {
|
||||
fastRaf(() => {
|
||||
@ -175,7 +176,13 @@ class PeerProfileAvatars {
|
||||
|
||||
this.container.append(this.avatars, this.info, this.tabs);
|
||||
|
||||
let cancel = false;
|
||||
attachClickEvent(this.container, (_e) => {
|
||||
if(cancel) {
|
||||
cancel = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = this.container.getBoundingClientRect();
|
||||
|
||||
const e = (_e as TouchEvent).touches ? (_e as TouchEvent).touches[0] : _e as MouseEvent;
|
||||
@ -184,7 +191,57 @@ class PeerProfileAvatars {
|
||||
const centerX = rect.right - (rect.width / 2);
|
||||
const toRight = x > centerX;
|
||||
|
||||
this.listLoader.go(toRight ? 1 : -1);
|
||||
// this.avatars.classList.remove('no-transition');
|
||||
// fastRaf(() => {
|
||||
this.listLoader.go(toRight ? 1 : -1);
|
||||
// });
|
||||
});
|
||||
|
||||
let width = 0, x = 0, lastDiffX = 0, lastIndex = 0, minX = 0;
|
||||
const swipeHandler = new SwipeHandler({
|
||||
element: this.avatars,
|
||||
onSwipe: (xDiff, yDiff) => {
|
||||
lastDiffX = xDiff;
|
||||
let lastX = x + xDiff * -2;
|
||||
if(lastX > 0) lastX = 0;
|
||||
else if(lastX < minX) lastX = minX;
|
||||
|
||||
this.avatars.style.transform = `translate3d(${lastX}px, 0, -1px) scale(2)`;
|
||||
//console.log(xDiff, yDiff);
|
||||
return false;
|
||||
},
|
||||
verifyTouchTarget: (e) => {
|
||||
if(this.tabs.classList.contains('hide')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onFirstSwipe: () => {
|
||||
const rect = this.avatars.getBoundingClientRect();
|
||||
width = rect.width;
|
||||
minX = -width * (this.tabs.childElementCount - 1);
|
||||
|
||||
/* lastIndex = whichChild(this.tabs.querySelector('.active'));
|
||||
x = -width * lastIndex; */
|
||||
x = rect.left - this.container.getBoundingClientRect().left;
|
||||
|
||||
this.avatars.style.transform = `translate3d(${x}px, 0, -1px) scale(2)`;
|
||||
|
||||
this.avatars.classList.add('no-transition');
|
||||
void this.avatars.offsetLeft; // reflow
|
||||
},
|
||||
onReset: () => {
|
||||
const addIndex = Math.ceil(Math.abs(lastDiffX) / (width / 2)) * (lastDiffX >= 0 ? 1 : -1);
|
||||
cancel = true;
|
||||
|
||||
//console.log(addIndex);
|
||||
|
||||
this.avatars.classList.remove('no-transition');
|
||||
fastRaf(() => {
|
||||
this.listLoader.go(addIndex);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -211,7 +268,7 @@ class PeerProfileAvatars {
|
||||
onJump: (item, older) => {
|
||||
const id = this.listLoader.index;
|
||||
//const nextId = Math.max(0, id);
|
||||
this.avatars.style.transform = `translateX(-${100 * id}%)`;
|
||||
this.avatars.style.transform = `translate3d(-${200 * id}%, 0, -1px) scale(2)`;
|
||||
|
||||
const activeTab = this.tabs.querySelector('.active');
|
||||
if(activeTab) activeTab.classList.remove('active');
|
||||
@ -235,6 +292,8 @@ class PeerProfileAvatars {
|
||||
if(this.tabs.childElementCount === 1) {
|
||||
tab.classList.add('active');
|
||||
}
|
||||
|
||||
this.tabs.classList.toggle('hide', this.tabs.childElementCount <= 1);
|
||||
}
|
||||
|
||||
public processItem = (photoId: string) => {
|
||||
@ -242,16 +301,19 @@ class PeerProfileAvatars {
|
||||
avatar.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar');
|
||||
|
||||
const photo = appPhotosManager.getPhoto(photoId);
|
||||
const img = new Image();
|
||||
img.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar-image');
|
||||
img.draggable = false;
|
||||
|
||||
if(photo) {
|
||||
appPhotosManager.preloadPhoto(photo, appPhotosManager.choosePhotoSize(photo, 420, 420, false)).then(() => {
|
||||
const img = new Image();
|
||||
renderImageFromUrl(img, photo.url, () => {
|
||||
avatar.append(img);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const photo = appPeersManager.getPeerPhoto(this.peerId);
|
||||
appProfileManager.putAvatar(avatar, this.peerId, photo, 'photo_big');
|
||||
appProfileManager.putAvatar(avatar, this.peerId, photo, 'photo_big', img);
|
||||
}
|
||||
|
||||
this.avatars.append(avatar);
|
||||
@ -636,19 +698,10 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
transition(+isSharedMedia);
|
||||
|
||||
if(!isSharedMedia) {
|
||||
this.searchSuper.goingHard = {};
|
||||
this.searchSuper.cleanScrollPositions();
|
||||
}
|
||||
};
|
||||
|
||||
this.scrollable.container.addEventListener('scroll', () => {
|
||||
if(this.profile.avatars) {
|
||||
const scrollTop = this.scrollable.scrollTop;
|
||||
const y = scrollTop / 2;
|
||||
this.profile.avatars.avatars.style.transform = `translateY(${y}px)`;
|
||||
//this.profile.avatars.tabs.style.transform = `translateY(${scrollTop}px)`;
|
||||
}
|
||||
});
|
||||
|
||||
const transition = TransitionSlider(transitionContainer, 'slide-fade', 400, null, false);
|
||||
|
||||
transition(0);
|
||||
@ -686,25 +739,30 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
|
||||
//this.container.prepend(this.closeBtn.parentElement);
|
||||
|
||||
this.searchSuper = new AppSearchSuper([{
|
||||
inputFilter: 'inputMessagesFilterPhotoVideo',
|
||||
name: 'SharedMediaTab2',
|
||||
type: 'media'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterDocument',
|
||||
name: 'SharedFilesTab2',
|
||||
type: 'files'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterUrl',
|
||||
name: 'SharedLinksTab2',
|
||||
type: 'links'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterMusic',
|
||||
name: 'SharedMusicTab2',
|
||||
type: 'music'
|
||||
}], this.scrollable/* , undefined, undefined, false */);
|
||||
|
||||
this.profile.element.append(this.searchSuper.container);
|
||||
this.searchSuper = new AppSearchSuper({
|
||||
mediaTabs: [{
|
||||
inputFilter: 'inputMessagesFilterEmpty',
|
||||
name: 'PeerMedia.Members',
|
||||
type: 'members'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterPhotoVideo',
|
||||
name: 'SharedMediaTab2',
|
||||
type: 'media'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterDocument',
|
||||
name: 'SharedFilesTab2',
|
||||
type: 'files'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterUrl',
|
||||
name: 'SharedLinksTab2',
|
||||
type: 'links'
|
||||
}, {
|
||||
inputFilter: 'inputMessagesFilterMusic',
|
||||
name: 'SharedMusicTab2',
|
||||
type: 'music'
|
||||
}],
|
||||
scrollable: this.scrollable
|
||||
});
|
||||
}
|
||||
|
||||
public renderNewMessages(peerId: number, mids: number[]) {
|
||||
@ -713,7 +771,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
if(!this.historiesStorage[peerId]) return;
|
||||
|
||||
mids = mids.slice().reverse(); // ! because it will be ascend sorted array
|
||||
for(const type of this.searchSuper.types) {
|
||||
for(const type of this.searchSuper.mediaTabs) {
|
||||
const inputFilter = type.inputFilter;
|
||||
const filtered = this.searchSuper.filterMessagesByType(mids.map(mid => appMessagesManager.getMessageByPeer(peerId, mid)), inputFilter);
|
||||
if(filtered.length) {
|
||||
@ -737,7 +795,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
if(!this.historiesStorage[peerId]) return;
|
||||
|
||||
for(const mid of mids) {
|
||||
for(const type of this.searchSuper.types) {
|
||||
for(const type of this.searchSuper.mediaTabs) {
|
||||
const inputFilter = type.inputFilter;
|
||||
|
||||
if(!this.historiesStorage[peerId][inputFilter]) continue;
|
||||
@ -773,6 +831,10 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
this.editBtn.style.display = 'none';
|
||||
this.searchSuper.cleanupHTML();
|
||||
this.searchSuper.selectTab(0, false);
|
||||
|
||||
if(!this.searchSuper.container.parentElement) {
|
||||
this.profile.element.append(this.searchSuper.container);
|
||||
}
|
||||
}
|
||||
|
||||
public setLoadMutex(promise: Promise<any>) {
|
||||
@ -800,6 +862,8 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
}
|
||||
|
||||
public fillProfileElements() {
|
||||
this.cleanupHTML();
|
||||
|
||||
this.profile.fillProfileElements();
|
||||
|
||||
if(this.peerId > 0) {
|
||||
|
@ -112,6 +112,7 @@ export default class SidebarSlider {
|
||||
}
|
||||
|
||||
this.removeTabFromHistory(tab);
|
||||
appNavigationController.removeByType(this.navigationType, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,34 +1,108 @@
|
||||
export default class SwipeHandler {
|
||||
private xDown: number;
|
||||
private yDown: number;
|
||||
import { cancelEvent } from "../helpers/dom";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { isTouchSupported } from "../helpers/touchSupport";
|
||||
|
||||
constructor(element: HTMLElement, private onSwipe: (xDiff: number, yDiff: number) => boolean, private verifyTouchTarget?: (evt: TouchEvent) => boolean) {
|
||||
element.addEventListener('touchstart', this.handleTouchStart, false);
|
||||
element.addEventListener('touchmove', this.handleTouchMove, false);
|
||||
const getEvent = (e: TouchEvent | MouseEvent) => {
|
||||
return (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent;
|
||||
};
|
||||
|
||||
const attachGlobalListenerTo = window;
|
||||
|
||||
export default class SwipeHandler {
|
||||
private element: HTMLElement;
|
||||
private onSwipe: (xDiff: number, yDiff: number) => boolean;
|
||||
private verifyTouchTarget: (evt: Touch | MouseEvent) => boolean;
|
||||
private onFirstSwipe: () => void;
|
||||
private onReset: () => void;
|
||||
|
||||
private hadMove = false;
|
||||
private xDown: number = null;
|
||||
private yDown: number = null;
|
||||
|
||||
constructor(options: {
|
||||
element: SwipeHandler['element'],
|
||||
onSwipe: SwipeHandler['onSwipe'],
|
||||
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
|
||||
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
|
||||
onReset?: SwipeHandler['onReset'],
|
||||
}) {
|
||||
safeAssign(this, options);
|
||||
|
||||
if(!isTouchSupported) {
|
||||
this.element.addEventListener('mousedown', this.handleStart, false);
|
||||
attachGlobalListenerTo.addEventListener('mouseup', this.reset);
|
||||
} else {
|
||||
this.element.addEventListener('touchstart', this.handleStart, false);
|
||||
attachGlobalListenerTo.addEventListener('touchend', this.reset);
|
||||
}
|
||||
}
|
||||
|
||||
handleTouchStart = (evt: TouchEvent) => {
|
||||
if(this.verifyTouchTarget && !this.verifyTouchTarget(evt)) {
|
||||
this.xDown = this.yDown = null;
|
||||
return;
|
||||
reset = (e?: Event) => {
|
||||
/* if(e) {
|
||||
cancelEvent(e);
|
||||
} */
|
||||
|
||||
if(isTouchSupported) {
|
||||
attachGlobalListenerTo.removeEventListener('touchmove', this.handleMove, {capture: true});
|
||||
} else {
|
||||
attachGlobalListenerTo.removeEventListener('mousemove', this.handleMove);
|
||||
this.element.style.cursor = '';
|
||||
}
|
||||
|
||||
const firstTouch = evt.touches[0];
|
||||
this.xDown = firstTouch.clientX;
|
||||
this.yDown = firstTouch.clientY;
|
||||
if(this.onReset && this.hadMove) {
|
||||
this.onReset();
|
||||
}
|
||||
|
||||
this.xDown = this.yDown = null;
|
||||
this.hadMove = false;
|
||||
};
|
||||
|
||||
handleTouchMove = (evt: TouchEvent) => {
|
||||
if(this.xDown === null || this.yDown === null) {
|
||||
return;
|
||||
handleStart = (_e: TouchEvent | MouseEvent) => {
|
||||
const e = getEvent(_e);
|
||||
if(this.verifyTouchTarget && !this.verifyTouchTarget(e)) {
|
||||
return this.reset();
|
||||
}
|
||||
|
||||
const xUp = evt.touches[0].clientX;
|
||||
const yUp = evt.touches[0].clientY;
|
||||
this.xDown = e.clientX;
|
||||
this.yDown = e.clientY;
|
||||
|
||||
if(isTouchSupported) {
|
||||
attachGlobalListenerTo.addEventListener('touchmove', this.handleMove, {passive: false, capture: true});
|
||||
} else {
|
||||
attachGlobalListenerTo.addEventListener('mousemove', this.handleMove, false);
|
||||
}
|
||||
};
|
||||
|
||||
handleMove = (_e: TouchEvent | MouseEvent) => {
|
||||
if(this.xDown === null || this.yDown === null) {
|
||||
return this.reset();
|
||||
}
|
||||
|
||||
cancelEvent(_e);
|
||||
|
||||
const e = getEvent(_e);
|
||||
const xUp = e.clientX;
|
||||
const yUp = e.clientY;
|
||||
|
||||
const xDiff = this.xDown - xUp;
|
||||
const yDiff = this.yDown - yUp;
|
||||
|
||||
if(!this.hadMove) {
|
||||
if(!xDiff && !yDiff) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hadMove = true;
|
||||
|
||||
if(!isTouchSupported) {
|
||||
this.element.style.cursor = 'grabbing';
|
||||
}
|
||||
|
||||
if(this.onFirstSwipe) {
|
||||
this.onFirstSwipe();
|
||||
}
|
||||
}
|
||||
|
||||
// if(Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
|
||||
// if(xDiff > 0) { /* left swipe */
|
||||
|
||||
@ -45,8 +119,7 @@ export default class SwipeHandler {
|
||||
|
||||
/* reset values */
|
||||
if(this.onSwipe(xDiff, yDiff)) {
|
||||
this.xDown = null;
|
||||
this.yDown = null;
|
||||
this.reset();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ const App = {
|
||||
id: 1025907,
|
||||
hash: '452b0359b988148995f22ff0f4229750',
|
||||
version: '0.4.0',
|
||||
langPackVersion: '0.0.9',
|
||||
langPackVersion: '0.1.0',
|
||||
langPack: 'macos',
|
||||
langPackCode: 'en',
|
||||
domains: [] as string[],
|
||||
|
16
src/helpers/middleware.ts
Normal file
16
src/helpers/middleware.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// * will change .cleaned and new instance will be created
|
||||
export const getMiddleware = () => {
|
||||
let cleanupObj = {cleaned: false};
|
||||
return {
|
||||
clean: () => {
|
||||
cleanupObj.cleaned = true;
|
||||
cleanupObj = {cleaned: false};
|
||||
},
|
||||
get: () => {
|
||||
const _cleanupObj = cleanupObj;
|
||||
return () => {
|
||||
return !_cleanupObj.cleaned;
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
@ -82,7 +82,7 @@
|
||||
<div class="sidebar-header__btn-container">
|
||||
<div class="animated-menu-icon"></div>
|
||||
<div class="btn-icon btn-menu-toggle rp sidebar-tools-button is-visible"></div>
|
||||
<div class="btn-icon rp sidebar-back-button"></div>
|
||||
<div class="btn-icon sidebar-back-button"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-content transition zoom-fade">
|
||||
|
@ -529,6 +529,7 @@ const lang = {
|
||||
"PeerInfo.SharedMedia": "Shared Media",
|
||||
"PeerInfo.Subscribers": "Subscribers",
|
||||
"PeerInfo.DeleteContact": "Delete Contact",
|
||||
"PeerMedia.Members": "Members",
|
||||
"PollResults.Title.Poll": "Poll Results",
|
||||
"PollResults.Title.Quiz": "Quiz Results",
|
||||
"PollResults.LoadMore": {
|
||||
|
@ -6,7 +6,6 @@ import { ripple } from "../../components/ripple";
|
||||
//import Scrollable from "../../components/scrollable";
|
||||
import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable";
|
||||
import { formatDateAccordingToTodayNew } from "../../helpers/date";
|
||||
import { escapeRegExp } from "../../helpers/string";
|
||||
import { isSafari } from "../../helpers/userAgent";
|
||||
import { logger, LogLevels } from "../logger";
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
@ -830,11 +829,17 @@ export class AppDialogsManager {
|
||||
|
||||
//const scrollTopWas = this.scroll.scrollTop;
|
||||
|
||||
const rect = this.scroll.container.getBoundingClientRect();
|
||||
const rectX = this.chatList.firstElementChild.getBoundingClientRect();
|
||||
const firstElementChild = this.chatList.firstElementChild;
|
||||
const rectContainer = this.scroll.container.getBoundingClientRect();
|
||||
const rectTarget = firstElementChild.getBoundingClientRect();
|
||||
const children = Array.from(this.scroll.splitUp.children) as HTMLElement[];
|
||||
const firstElement = findUpTag(document.elementFromPoint(Math.ceil(rectX.x), Math.ceil(rect.y + 1)), 'LI') as HTMLElement;
|
||||
const lastElement = findUpTag(document.elementFromPoint(Math.ceil(rectX.x), Math.floor(rect.y + rect.height - 1)), 'LI') as HTMLElement;
|
||||
|
||||
const offsetTop = this.folders.container.offsetTop;
|
||||
const firstY = rectContainer.y + offsetTop;
|
||||
const lastY = rectContainer.y;
|
||||
|
||||
const firstElement = findUpTag(document.elementFromPoint(Math.ceil(rectTarget.x), Math.ceil(firstY + 1)), firstElementChild.tagName) as HTMLElement;
|
||||
const lastElement = findUpTag(document.elementFromPoint(Math.ceil(rectTarget.x), Math.floor(lastY + rectContainer.height - 1)), firstElementChild.tagName) as HTMLElement;
|
||||
|
||||
//alert('got element:' + rect.y);
|
||||
|
||||
@ -845,7 +850,7 @@ export class AppDialogsManager {
|
||||
//alert('got element:' + !!firstElement);
|
||||
|
||||
const firstElementRect = firstElement.getBoundingClientRect();
|
||||
const elementOverflow = firstElementRect.y - rect.y;
|
||||
const elementOverflow = firstElementRect.y - firstY;
|
||||
|
||||
const sliced: HTMLElement[] = [];
|
||||
const firstIndex = children.indexOf(firstElement);
|
||||
|
@ -105,13 +105,7 @@
|
||||
height: calc(100% - 44px);
|
||||
|
||||
#folders-container {
|
||||
li:first-child {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
> div {
|
||||
background-color: transparent;
|
||||
}
|
||||
margin-top: .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,6 +199,10 @@
|
||||
|
||||
#folders-container {
|
||||
min-height: 100%;
|
||||
|
||||
> div {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-slider {
|
||||
|
@ -3,9 +3,10 @@
|
||||
&-container {
|
||||
width: 100%;
|
||||
height: 26.25rem;
|
||||
overflow: hidden;
|
||||
//overflow: hidden;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
/* &:before, &:after {
|
||||
position: absolute;
|
||||
@ -20,8 +21,18 @@
|
||||
height: inherit;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
transform: translateX(0);
|
||||
//transition: transform .2s ease-in-out;
|
||||
transform: translateZ(-1px) scale(2);
|
||||
transform-origin: left top;
|
||||
transition: transform .2s ease-in-out;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
@ -32,11 +43,12 @@
|
||||
display: flex;
|
||||
background-color: #000;
|
||||
|
||||
/* img, video {
|
||||
&-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
} */
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-info {
|
||||
@ -60,6 +72,10 @@
|
||||
.profile-subtitle {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.online {
|
||||
color: inherit !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-tabs {
|
||||
@ -118,6 +134,8 @@
|
||||
}
|
||||
|
||||
.sidebar-left-section {
|
||||
position: relative;
|
||||
background-color: var(--surface-color);
|
||||
//padding-top: .5625rem;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
@ -102,6 +102,11 @@
|
||||
max-height: calc((var(--vh, 1vh) * 100) - 100% - 56px);
|
||||
}
|
||||
} */
|
||||
.scrollable {
|
||||
perspective: 1px;
|
||||
perspective-origin: left top;
|
||||
}
|
||||
|
||||
.search-super {
|
||||
top: 100%;
|
||||
min-height: calc((var(--vh, 1vh) * 100) - 56px);
|
||||
@ -124,6 +129,7 @@
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--surface-color);
|
||||
|
||||
.search-group__show-more {
|
||||
color: var(--primary-color);
|
||||
@ -344,16 +350,13 @@
|
||||
min-height: 58px;
|
||||
|
||||
.preview {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
border-radius: 5px;
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
border-radius: .375rem;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
|
||||
&.empty {
|
||||
display: flex;
|
||||
@ -364,6 +367,12 @@
|
||||
text-transform: uppercase;
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.media-photo {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.url {
|
||||
|
@ -999,7 +999,7 @@ middle-ellipsis-element {
|
||||
}
|
||||
|
||||
.row {
|
||||
min-height: 3rem;
|
||||
min-height: 3.5rem;
|
||||
position: relative;
|
||||
padding: .6875rem 1rem;
|
||||
display: flex;
|
||||
|
Loading…
Reference in New Issue
Block a user