Browse Source

Swipe animation for profile avatars

Search refactor
Fix dialogs slicing
master
Eduard Kuzmenko 4 years ago
parent
commit
3119e7788a
  1. 56
      src/components/appMediaViewer.ts
  2. 2764
      src/components/appMediaViewerNew.ts
  3. 414
      src/components/appSearchSuper..ts
  4. 2
      src/components/chat/bubbles.ts
  5. 58
      src/components/sidebarLeft/index.ts
  6. 136
      src/components/sidebarRight/tabs/sharedMedia.ts
  7. 1
      src/components/slider.ts
  8. 109
      src/components/swipeHandler.ts
  9. 2
      src/config/app.ts
  10. 16
      src/helpers/middleware.ts
  11. 2
      src/index.hbs
  12. 1
      src/lang.ts
  13. 17
      src/lib/appManagers/appDialogsManager.ts
  14. 12
      src/scss/partials/_leftSidebar.scss
  15. 28
      src/scss/partials/_profile.scss
  16. 21
      src/scss/partials/_rightSidebar.scss
  17. 2
      src/scss/style.scss

56
src/components/appMediaViewer.ts

@ -194,39 +194,43 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType @@ -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;
}
return true;
}
const percentsY = Math.abs(yDiff) / appPhotosManager.windowH;
if(percentsY > .2 || yDiff > 125) {
this.buttons.close.click();
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;
}
},
verifyTouchTarget: (evt) => {
// * Fix for seek input
if((evt.target as HTMLElement).tagName === 'INPUT' || findUpClassName(evt.target, 'media-viewer-caption')) {
return false;
}
return true;
return true;
}
});
}
}

2764
src/components/appMediaViewerNew.ts

File diff suppressed because it is too large Load Diff

414
src/components/appSearchSuper..ts

@ -1,6 +1,6 @@ @@ -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 @@ -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 = { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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);
@ -170,23 +181,23 @@ export default class AppSearchSuper { @@ -170,23 +181,23 @@ export default class AppSearchSuper {
this.onTransitionStart();
}
this.goingHard[this.type] = {scrollTop: this.scrollable.scrollTop, scrollHeight: this.scrollable.scrollHeight};
this.mediaTab.scroll = {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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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];
@ -808,151 +818,160 @@ export default class AppSearchSuper { @@ -808,151 +818,160 @@ export default class AppSearchSuper {
} else return Promise.resolve();
}
public load(single = false, justLoad = false) {
// if(testScroll/* || 1 === 1 */) {
// return;
// }
private loadType(mediaTab: SearchSuperMediaTab, justLoad: boolean, loadCount: number, middleware: () => boolean) {
const type = mediaTab.inputFilter;
//return;
if(this.loadPromises[type]) {
return this.loadPromises[type];
}
const peerId = this.searchContext.peerId;
this.log('load', single, peerId, this.loadPromises);
const history = this.historyStorage[type] ?? (this.historyStorage[type] = []);
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));
if(type === 'inputMessagesFilterEmpty' && !history.length) {
if(!this.loadedChats) {
this.loadChats();
this.loadedChats = true;
}
if(!typesToLoad.length) return;
if(!this.searchContext.query.trim() && !this.searchContext.peerId && !this.searchContext.minDate) {
this.loaded[type] = true;
return Promise.resolve();
}
}
const loadCount = justLoad ? 50 : Math.round((appPhotosManager.windowH / 130 | 0) * 3 * 1.25); // that's good for all types
const logStr = 'load [' + type + ']: ';
const historyStorage = this.historyStorage ?? (this.historyStorage = {});
// 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;
const middleware = this.getMiddleware();
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;
const promises: Promise<any>[] = typesToLoad.map(type => {
if(this.loadPromises[type]) return this.loadPromises[type];
messages.push(...this.filterMessagesByType(ids.map(m => appMessagesManager.getMessageByPeer(m.peerId, m.mid)), type));
} while(slicedLength < loadCount && used < history.length);
const history = historyStorage[type] ?? (historyStorage[type] = []);
// если перебор
/* if(slicedLength > loadCount) {
let diff = messages.length - loadCount;
messages = messages.slice(0, messages.length - diff);
used -= diff;
} */
if(type === 'inputMessagesFilterEmpty' && !history.length) {
if(!this.loadedChats) {
this.loadChats();
this.loadedChats = true;
}
this.usedFromHistory[type] = used;
//if(messages.length) {
return this.performSearchResult(messages, type).finally(() => {
setTimeout(() => {
this.scrollable.checkForTriggers();
}, 0);
});
//}
if(!this.searchContext.query.trim() && !this.searchContext.peerId && !this.searchContext.minDate) {
this.loaded[type] = true;
return Promise.resolve();
}
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;
}
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);
});
//}
// ! Фикс случая, когда не загружаются документы при открытой панели разработчиков (происходит из-за того, что не совпадают критерии отбора документов в 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();
}
let maxId = history.length ? history[history.length - 1].mid : 0;
//this.log(logStr + 'search house of glass pre', type, maxId);
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);
});
}
//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})));
//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;
});
}
this.log(logStr + 'search house of glass', type, value);
public load(single = false, justLoad = false) {
// if(testScroll/* || 1 === 1 */) {
// return;
// }
if(!middleware()) {
//this.log.warn('peer changed');
return;
}
//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;
}
const peerId = this.searchContext.peerId;
this.log('load', single, peerId, this.loadPromises);
this.nextRates[type] = value.next_rate;
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(justLoad) {
return Promise.resolve();
}
if(!toLoad.length) {
return;
}
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);
});
}
const loadCount = justLoad ? 50 : Math.round((appPhotosManager.windowH / 130 | 0) * 3 * 1.25); // that's good for all types
const middleware = this.middleware.get();
//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 { @@ -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 { @@ -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);
@ -1061,14 +1085,6 @@ export default class AppSearchSuper { @@ -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 { @@ -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,

2
src/components/chat/bubbles.ts

@ -829,7 +829,7 @@ export default class ChatBubbles { @@ -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) {

58
src/components/sidebarLeft/index.ts

@ -193,31 +193,37 @@ export class AppSidebarLeft extends SidebarSlider { @@ -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 { @@ -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)

136
src/components/sidebarRight/tabs/sharedMedia.ts

@ -10,7 +10,7 @@ import AppSearchSuper, { SearchSuperType } from "../../appSearchSuper."; @@ -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"; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -800,6 +862,8 @@ export default class AppSharedMediaTab extends SliderSuperTab {
}
public fillProfileElements() {
this.cleanupHTML();
this.profile.fillProfileElements();
if(this.peerId > 0) {

1
src/components/slider.ts

@ -112,6 +112,7 @@ export default class SidebarSlider { @@ -112,6 +112,7 @@ export default class SidebarSlider {
}
this.removeTabFromHistory(tab);
appNavigationController.removeByType(this.navigationType, true);
}
}

109
src/components/swipeHandler.ts

@ -1,34 +1,108 @@ @@ -1,34 +1,108 @@
import { cancelEvent } from "../helpers/dom";
import { safeAssign } from "../helpers/object";
import { isTouchSupported } from "../helpers/touchSupport";
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 xDown: number;
private yDown: number;
private element: HTMLElement;
private onSwipe: (xDiff: number, yDiff: number) => boolean;
private verifyTouchTarget: (evt: Touch | MouseEvent) => boolean;
private onFirstSwipe: () => void;
private onReset: () => void;
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);
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 = '';
}
if(this.onReset && this.hadMove) {
this.onReset();
}
const firstTouch = evt.touches[0];
this.xDown = firstTouch.clientX;
this.yDown = firstTouch.clientY;
this.xDown = this.yDown = null;
this.hadMove = false;
};
handleTouchMove = (evt: TouchEvent) => {
handleStart = (_e: TouchEvent | MouseEvent) => {
const e = getEvent(_e);
if(this.verifyTouchTarget && !this.verifyTouchTarget(e)) {
return this.reset();
}
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;
return this.reset();
}
const xUp = evt.touches[0].clientX;
const yUp = evt.touches[0].clientY;
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 { @@ -45,8 +119,7 @@ export default class SwipeHandler {
/* reset values */
if(this.onSwipe(xDiff, yDiff)) {
this.xDown = null;
this.yDown = null;
this.reset();
}
};
}

2
src/config/app.ts

@ -2,7 +2,7 @@ const App = { @@ -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

@ -0,0 +1,16 @@ @@ -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;
};
}
};
};

2
src/index.hbs

@ -82,7 +82,7 @@ @@ -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">

1
src/lang.ts

@ -529,6 +529,7 @@ const lang = { @@ -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": {

17
src/lib/appManagers/appDialogsManager.ts

@ -6,7 +6,6 @@ import { ripple } from "../../components/ripple"; @@ -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 { @@ -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 { @@ -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);

12
src/scss/partials/_leftSidebar.scss

@ -105,13 +105,7 @@ @@ -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 @@ @@ -205,6 +199,10 @@
#folders-container {
min-height: 100%;
> div {
background-color: transparent;
}
}
.sidebar-slider {

28
src/scss/partials/_profile.scss

@ -3,9 +3,10 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -60,6 +72,10 @@
.profile-subtitle {
opacity: .7;
}
.online {
color: inherit !important;
}
}
&-tabs {
@ -118,6 +134,8 @@ @@ -118,6 +134,8 @@
}
.sidebar-left-section {
position: relative;
background-color: var(--surface-color);
//padding-top: .5625rem;
padding-bottom: 0;
}

21
src/scss/partials/_rightSidebar.scss

@ -102,6 +102,11 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -364,6 +367,12 @@
text-transform: uppercase;
background-color: var(--primary-color);
}
.media-photo {
object-fit: cover;
width: 100%;
height: 100%;
}
}
.url {

2
src/scss/style.scss

@ -999,7 +999,7 @@ middle-ellipsis-element { @@ -999,7 +999,7 @@ middle-ellipsis-element {
}
.row {
min-height: 3rem;
min-height: 3.5rem;
position: relative;
padding: .6875rem 1rem;
display: flex;

Loading…
Cancel
Save