Profile improvements
Hide scrollbar on auth pages
This commit is contained in:
parent
41ebab81c7
commit
bfc1db799c
@ -39,6 +39,8 @@ import { forEachReverse } from "../helpers/array";
|
||||
import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia";
|
||||
import findUpClassName from "../helpers/dom/findUpClassName";
|
||||
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
|
||||
import findUpAsChild from "../helpers/dom/findUpAsChild";
|
||||
import getVisibleRect from "../helpers/dom/getVisibleRect";
|
||||
|
||||
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
|
||||
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
|
||||
@ -332,10 +334,6 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
protected async setMoverToTarget(target: HTMLElement, closing = false, fromRight = 0) {
|
||||
const mover = this.content.mover;
|
||||
|
||||
if(!target) {
|
||||
target = this.content.media;
|
||||
}
|
||||
|
||||
if(!closing) {
|
||||
mover.innerHTML = '';
|
||||
//mover.append(this.buttons.prev, this.buttons.next);
|
||||
@ -361,15 +359,43 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
let rect: DOMRect;
|
||||
if(target) {
|
||||
if(target instanceof AvatarElement) {
|
||||
if(target instanceof AvatarElement || target.classList.contains('grid-item')) {
|
||||
realParent = target;
|
||||
rect = target.getBoundingClientRect();
|
||||
} else if(target instanceof SVGImageElement || target.parentElement instanceof SVGForeignObjectElement) {
|
||||
realParent = findUpClassName(target, 'attachment');
|
||||
rect = realParent.getBoundingClientRect();
|
||||
} else {
|
||||
} else if(target.classList.contains('profile-avatars-avatar')) {
|
||||
realParent = findUpClassName(target, 'profile-avatars-container');
|
||||
rect = realParent.getBoundingClientRect();
|
||||
|
||||
// * if not active avatar
|
||||
if(closing && target.getBoundingClientRect().left !== rect.left) {
|
||||
target = realParent = rect = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!target) {
|
||||
target = this.content.media;
|
||||
}
|
||||
|
||||
if(!rect) {
|
||||
realParent = target.parentElement as HTMLElement;
|
||||
rect = target.getBoundingClientRect();
|
||||
}
|
||||
|
||||
let needOpacity = false;
|
||||
if(target !== this.content.media && !target.classList.contains('profile-avatars-avatar')) {
|
||||
const overflowElement = findUpClassName(realParent, 'scrollable');
|
||||
const visibleRect = getVisibleRect(realParent, overflowElement);
|
||||
|
||||
if(closing && (!visibleRect || visibleRect.overflow.vertical === 2 || visibleRect.overflow.horizontal === 2)) {
|
||||
target = this.content.media;
|
||||
realParent = target.parentElement as HTMLElement;
|
||||
rect = target.getBoundingClientRect();
|
||||
} else if(visibleRect && (visibleRect.overflow.vertical === 1 || visibleRect.overflow.horizontal === 1)) {
|
||||
needOpacity = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,6 +470,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
mover.style.transform = transform;
|
||||
|
||||
needOpacity && (mover.style.opacity = '0'/* !closing ? '0' : '' */);
|
||||
|
||||
/* if(wasActive) {
|
||||
this.log('setMoverToTarget', mover.style.transform);
|
||||
} */
|
||||
@ -612,6 +640,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
return ret;
|
||||
}
|
||||
|
||||
mover.classList.toggle('opening', !closing);
|
||||
|
||||
//await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
//await new Promise((resolve) => window.requestAnimationFrame(resolve));
|
||||
// * одного RAF'а недостаточно, иногда анимация с одним не срабатывает (преимущественно на мобильных)
|
||||
@ -624,6 +654,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
mover.style.transform = `translate3d(${containerRect.left}px,${containerRect.top}px,0) scale3d(1,1,1)`;
|
||||
//mover.style.transform = `translate(-50%,-50%) scale(1,1)`;
|
||||
needOpacity && (mover.style.opacity = ''/* closing ? '0' : '' */);
|
||||
|
||||
if(aspecter) {
|
||||
this.setFullAspect(aspecter, containerRect, rect);
|
||||
@ -1109,7 +1140,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
onAnimationEnd.then(() => {
|
||||
if(!media.url) {
|
||||
this.preloader.attach(mover, true, cancellablePromise);
|
||||
this.preloader.attachPromise(cancellablePromise);
|
||||
//this.preloader.attach(mover, true, cancellablePromise);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1157,9 +1189,11 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
}
|
||||
}
|
||||
|
||||
this.preloader.detach();
|
||||
//this.preloader.detach();
|
||||
}).catch(err => {
|
||||
this.log.error(err);
|
||||
this.preloader.attach(mover);
|
||||
this.preloader.setManual();
|
||||
});
|
||||
|
||||
return cancellablePromise;
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { formatDateAccordingToToday, months } from "../helpers/date";
|
||||
import { positionElementByIndex, isInDOM, replaceContent } from "../helpers/dom";
|
||||
import { positionElementByIndex } from "../helpers/dom";
|
||||
import { copy, getObjectKeysAndSort, safeAssign } from "../helpers/object";
|
||||
import { escapeRegExp, limitSymbols } from "../helpers/string";
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
@ -124,7 +124,7 @@ export default class AppSearchSuper {
|
||||
this.container.classList.add('search-super');
|
||||
|
||||
const navScrollableContainer = document.createElement('div');
|
||||
navScrollableContainer.classList.add('search-super-tabs-scrollable', 'menu-horizontal-scrollable');
|
||||
navScrollableContainer.classList.add('search-super-tabs-scrollable', 'menu-horizontal-scrollable', 'sticky');
|
||||
|
||||
const navScrollable = new ScrollableX(navScrollableContainer);
|
||||
|
||||
|
@ -9,7 +9,7 @@ import appProfileManager from "../lib/appManagers/appProfileManager";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import { attachClickEvent, cancelEvent } from "../helpers/dom";
|
||||
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
|
||||
import { Photo } from "../layer";
|
||||
import { Message, Photo } from "../layer";
|
||||
import appPeersManager from "../lib/appManagers/appPeersManager";
|
||||
//import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
||||
|
||||
@ -21,8 +21,72 @@ const onAvatarUpdate = (peerId: number) => {
|
||||
});
|
||||
};
|
||||
|
||||
rootScope.on('avatar_update', onAvatarUpdate);
|
||||
rootScope.on('peer_title_edit', onAvatarUpdate);
|
||||
rootScope.on('avatar_update', onAvatarUpdate);
|
||||
rootScope.on('peer_title_edit', onAvatarUpdate);
|
||||
|
||||
export async function openAvatarViewer(target: HTMLElement, peerId: number, middleware: () => boolean, message?: any, prevTargets?: {element: HTMLElement, item: string | Message.messageService}[], nextTargets?: typeof prevTargets) {
|
||||
const photo = await appProfileManager.getFullPhoto(peerId);
|
||||
if(!middleware() || !photo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const getTarget = () => {
|
||||
const good = Array.from(target.querySelectorAll('img')).find(img => !img.classList.contains('emoji'));
|
||||
return good ? target : null;
|
||||
};
|
||||
|
||||
if(peerId < 0) {
|
||||
const hadMessage = !!message;
|
||||
const inputFilter = 'inputMessagesFilterChatPhotos';
|
||||
if(!message) {
|
||||
message = await appMessagesManager.getSearch({
|
||||
peerId,
|
||||
inputFilter: {_: inputFilter},
|
||||
maxId: 0,
|
||||
limit: 1
|
||||
}).then(value => {
|
||||
//console.log(lol);
|
||||
// ! by descend
|
||||
return value.history[0];
|
||||
});
|
||||
|
||||
if(!middleware()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(message) {
|
||||
// ! гений в деле, костылируем (но это гениально)
|
||||
const messagePhoto = message.action.photo;
|
||||
if(messagePhoto.id !== photo.id) {
|
||||
if(!hadMessage) {
|
||||
message = appMessagesManager.generateFakeAvatarMessage(peerId, photo);
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const f = (arr: typeof prevTargets) => arr.map(el => ({
|
||||
element: el.element,
|
||||
mid: (el.item as Message.messageService).mid,
|
||||
peerId: (el.item as Message.messageService).peerId
|
||||
}));
|
||||
|
||||
new AppMediaViewer()
|
||||
.setSearchContext({
|
||||
peerId,
|
||||
inputFilter,
|
||||
})
|
||||
.openMedia(message, getTarget(), undefined, undefined, prevTargets ? f(prevTargets) : undefined, nextTargets ? f(nextTargets) : undefined);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(photo) {
|
||||
new AppMediaViewerAvatar(peerId).openMedia(photo.id, getTarget());
|
||||
}
|
||||
}
|
||||
|
||||
export default class AvatarElement extends HTMLElement {
|
||||
private peerId: number;
|
||||
@ -51,64 +115,7 @@ export default class AvatarElement extends HTMLElement {
|
||||
//console.log('avatar clicked');
|
||||
const peerId = this.peerId;
|
||||
loading = true;
|
||||
|
||||
const photo = await appProfileManager.getFullPhoto(this.peerId);
|
||||
if(this.peerId !== peerId || !photo) {
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(peerId < 0) {
|
||||
const maxId = Number.MAX_SAFE_INTEGER;
|
||||
const inputFilter = 'inputMessagesFilterChatPhotos';
|
||||
let message: any = await appMessagesManager.getSearch({
|
||||
peerId,
|
||||
inputFilter: {_: inputFilter},
|
||||
maxId,
|
||||
limit: 2,
|
||||
backLimit: 1
|
||||
}).then(value => {
|
||||
//console.log(lol);
|
||||
// ! by descend
|
||||
return value.history[0];
|
||||
});
|
||||
|
||||
if(message) {
|
||||
// ! гений в деле, костылируем (но это гениально)
|
||||
const messagePhoto = message.action.photo;
|
||||
if(messagePhoto.id !== photo.id) {
|
||||
message = {
|
||||
_: 'message',
|
||||
mid: maxId,
|
||||
media: {
|
||||
_: 'messageMediaPhoto',
|
||||
photo: photo
|
||||
},
|
||||
peerId,
|
||||
date: (photo as Photo.photo).date,
|
||||
fromId: peerId
|
||||
};
|
||||
|
||||
appMessagesManager.getMessagesStorage(peerId)[maxId] = message;
|
||||
}
|
||||
|
||||
const good = Array.from(this.querySelectorAll('img')).find(img => !img.classList.contains('emoji'));
|
||||
new AppMediaViewer()
|
||||
.setSearchContext({
|
||||
peerId,
|
||||
inputFilter,
|
||||
})
|
||||
.openMedia(message, good ? this : null);
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(photo) {
|
||||
const good = Array.from(this.querySelectorAll('img')).find(img => !img.classList.contains('emoji'));
|
||||
new AppMediaViewerAvatar(peerId).openMedia(photo.id, good ? this : null);
|
||||
}
|
||||
|
||||
await openAvatarViewer(this, this.peerId, () => this.peerId === peerId);
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
|
||||
import type { AppImManager } from "../../lib/appManagers/appImManager";
|
||||
import type { AppMessagesManager, HistoryResult, MyMessage } from "../../lib/appManagers/appMessagesManager";
|
||||
import type { AppStickersManager } from "../../lib/appManagers/appStickersManager";
|
||||
import type { AppUsersManager } from "../../lib/appManagers/appUsersManager";
|
||||
@ -14,6 +14,7 @@ import type { AppDocsManager } from "../../lib/appManagers/appDocsManager";
|
||||
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
|
||||
import type sessionStorage from '../../lib/sessionStorage';
|
||||
import type Chat from "./chat";
|
||||
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
|
||||
import { cancelEvent, whichChild, getElementByPoint, attachClickEvent, positionElementByIndex, reflowScrollableElement } from "../../helpers/dom";
|
||||
import { getObjectKeysAndSort } from "../../helpers/object";
|
||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||
@ -28,7 +29,6 @@ import ProgressivePreloader from "../preloader";
|
||||
import Scrollable from "../scrollable";
|
||||
import StickyIntersector from "../stickyIntersector";
|
||||
import animationIntersector from "../animationIntersector";
|
||||
import { months } from "../../helpers/date";
|
||||
import RichTextProcessor from "../../lib/richtextprocessor";
|
||||
import mediaSizes from "../../helpers/mediaSizes";
|
||||
import { isAndroid, isApple, isSafari } from "../../helpers/userAgent";
|
||||
@ -1415,15 +1415,15 @@ export default class ChatBubbles {
|
||||
topMessage = 0;
|
||||
}
|
||||
|
||||
let readMaxId = 0;//, savedPosition: ReturnType<AppImManager['getChatSavedPosition']>;
|
||||
let readMaxId = 0, savedPosition: ReturnType<AppImManager['getChatSavedPosition']>;
|
||||
if(!isTarget) {
|
||||
/* if(!samePeer) {
|
||||
if(!samePeer) {
|
||||
savedPosition = this.chat.appImManager.getChatSavedPosition(this.chat);
|
||||
}
|
||||
|
||||
if(savedPosition) {
|
||||
lastMsgId = savedPosition.mid;
|
||||
} else */if(topMessage) {
|
||||
|
||||
} else if(topMessage) {
|
||||
readMaxId = this.appMessagesManager.getReadMaxIdIfUnread(peerId, this.chat.threadId);
|
||||
if(/* dialog.unread_count */readMaxId && !samePeer) {
|
||||
lastMsgId = readMaxId;
|
||||
@ -1497,7 +1497,19 @@ export default class ChatBubbles {
|
||||
|
||||
this.lazyLoadQueue.lock();
|
||||
|
||||
const {promise, cached} = this.getHistory(lastMsgId, true, isJump, additionMsgId);
|
||||
let result: ReturnType<ChatBubbles['getHistory']>;
|
||||
if(!savedPosition) {
|
||||
result = this.getHistory(lastMsgId, true, isJump, additionMsgId);
|
||||
} else {
|
||||
result = {
|
||||
promise: getHeavyAnimationPromise().then(() => {
|
||||
return this.performHistoryResult(savedPosition.mids, true, false, undefined);
|
||||
}) as any,
|
||||
cached: true
|
||||
};
|
||||
}
|
||||
|
||||
const {promise, cached} = result;
|
||||
|
||||
// clear
|
||||
if(!cached) {
|
||||
@ -1533,8 +1545,9 @@ export default class ChatBubbles {
|
||||
this.lazyLoadQueue.unlock();
|
||||
|
||||
//if(dialog && lastMsgID && lastMsgID !== topMessage && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) {
|
||||
/* if(savedPosition) {
|
||||
const mountedByLastMsgId = this.getMountedBubble(lastMsgId);
|
||||
if(savedPosition) {
|
||||
this.scrollable.scrollTop = savedPosition.top;
|
||||
/* const mountedByLastMsgId = this.getMountedBubble(lastMsgId);
|
||||
let bubble: HTMLElement = mountedByLastMsgId?.bubble;
|
||||
if(!bubble?.parentElement) {
|
||||
bubble = this.findNextMountedBubbleByMsgId(lastMsgId);
|
||||
@ -1544,8 +1557,8 @@ export default class ChatBubbles {
|
||||
const top = bubble.getBoundingClientRect().top;
|
||||
const distance = savedPosition.top - top;
|
||||
this.scrollable.scrollTop += distance;
|
||||
}
|
||||
} else */if((topMessage && isJump) || isTarget) {
|
||||
} */
|
||||
} else if((topMessage && isJump) || isTarget) {
|
||||
const fromUp = maxBubbleId > 0 && (maxBubbleId < lastMsgId || lastMsgId < 0);
|
||||
const followingUnread = readMaxId === lastMsgId && !isTarget;
|
||||
if(!fromUp && samePeer) {
|
||||
|
@ -251,8 +251,10 @@ export default class Chat extends EventListenerBase<{
|
||||
}
|
||||
});
|
||||
|
||||
appSidebarRight.sharedMediaTab.setLoadMutex(this.setPeerPromise);
|
||||
appSidebarRight.sharedMediaTab.loadSidebarMedia(true);
|
||||
if(!samePeer) {
|
||||
appSidebarRight.sharedMediaTab.setLoadMutex(this.setPeerPromise);
|
||||
appSidebarRight.sharedMediaTab.loadSidebarMedia(true);
|
||||
}
|
||||
/* this.setPeerPromise.then(() => {
|
||||
appSidebarRight.sharedMediaTab.loadSidebarMedia(false);
|
||||
}); */
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { generatePathData } from "../../helpers/dom";
|
||||
import { i18n, LangPackKey } from "../../lib/langPack";
|
||||
|
||||
export default class ChatDragAndDrop {
|
||||
container: HTMLDivElement;
|
||||
@ -14,8 +15,8 @@ export default class ChatDragAndDrop {
|
||||
|
||||
constructor(appendTo: HTMLElement, private options: {
|
||||
icon: string,
|
||||
header: string,
|
||||
subtitle: string,
|
||||
header: LangPackKey,
|
||||
subtitle: LangPackKey,
|
||||
onDrop: (e: DragEvent) => void
|
||||
}) {
|
||||
this.container = document.createElement('div');
|
||||
@ -35,11 +36,11 @@ export default class ChatDragAndDrop {
|
||||
|
||||
const dropHeader = document.createElement('div');
|
||||
dropHeader.classList.add('drop-header');
|
||||
dropHeader.innerHTML = options.header;//'Drop files here to send them';
|
||||
dropHeader.append(i18n(options.header));
|
||||
|
||||
const dropSubtitle = document.createElement('div');
|
||||
dropSubtitle.classList.add('drop-subtitle');
|
||||
dropSubtitle.innerHTML = options.subtitle;//'without compression';
|
||||
dropSubtitle.append(i18n(options.subtitle));
|
||||
|
||||
this.svg.append(this.path);
|
||||
this.outlineWrapper.append(this.svg);
|
||||
|
@ -332,7 +332,9 @@ export default class ChatInput {
|
||||
|
||||
this.newMessageWrapper.append(...[this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean));
|
||||
|
||||
this.rowsWrapper.append(this.replyElements.container, this.newMessageWrapper);
|
||||
this.rowsWrapper.append(this.replyElements.container);
|
||||
this.stickersHelper = new StickersHelper(this.rowsWrapper);
|
||||
this.rowsWrapper.append(this.newMessageWrapper);
|
||||
|
||||
this.btnCancelRecord = ButtonIcon('delete danger btn-circle z-depth-1 btn-record-cancel');
|
||||
|
||||
@ -389,8 +391,6 @@ export default class ChatInput {
|
||||
}
|
||||
}, {passive: false, capture: true}); */
|
||||
|
||||
this.stickersHelper = new StickersHelper(this.rowsWrapper);
|
||||
|
||||
this.listenerSetter.add(rootScope, 'settings_updated', () => {
|
||||
if(this.stickersHelper) {
|
||||
if(!rootScope.settings.stickers.suggest) {
|
||||
|
@ -179,10 +179,12 @@ export default class ChatSelection {
|
||||
checkboxField.label.classList.add('bubble-select-checkbox');
|
||||
|
||||
// * if it is a render of new message
|
||||
const mid = +bubble.dataset.mid;
|
||||
if(this.selectedMids.has(mid) && (!isGrouped || this.isGroupedMidsSelected(mid))) {
|
||||
checkboxField.input.checked = true;
|
||||
bubble.classList.add('is-selected');
|
||||
if(this.isSelecting) { // ! avoid breaking animation on start
|
||||
const mid = +bubble.dataset.mid;
|
||||
if(this.selectedMids.has(mid) && (!isGrouped || this.isGroupedMidsSelected(mid))) {
|
||||
checkboxField.input.checked = true;
|
||||
bubble.classList.add('is-selected');
|
||||
}
|
||||
}
|
||||
|
||||
if(bubble.classList.contains('document-container')) {
|
||||
|
@ -23,7 +23,10 @@ export default class StickersHelper {
|
||||
private lastEmoticon = '';
|
||||
|
||||
constructor(private appendTo: HTMLElement) {
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add('stickers-helper', 'z-depth-1');
|
||||
|
||||
this.appendTo.append(this.container);
|
||||
}
|
||||
|
||||
public checkEmoticon(emoticon: string) {
|
||||
@ -32,7 +35,9 @@ export default class StickersHelper {
|
||||
if(this.lastEmoticon && !emoticon) {
|
||||
if(this.container) {
|
||||
SetTransition(this.container, 'is-visible', false, 200, () => {
|
||||
this.stickersContainer.innerHTML = '';
|
||||
if(this.stickersContainer) {
|
||||
this.stickersContainer.innerHTML = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -86,8 +91,6 @@ export default class StickersHelper {
|
||||
}
|
||||
|
||||
private init() {
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add('stickers-helper', 'z-depth-1');
|
||||
this.container.addEventListener('click', (e) => {
|
||||
if(!findUpClassName(e.target, 'super-sticker')) {
|
||||
return;
|
||||
@ -104,7 +107,5 @@ export default class StickersHelper {
|
||||
this.scrollable = new Scrollable(this.container);
|
||||
this.lazyLoadQueue = new LazyLoadQueue();
|
||||
this.superStickerRenderer = new SuperStickerRenderer(this.lazyLoadQueue, CHAT_ANIMATION_GROUP);
|
||||
|
||||
this.appendTo.append(this.container);
|
||||
}
|
||||
}
|
||||
|
@ -447,7 +447,8 @@ export default class ChatTopbar {
|
||||
public setTitle(count?: number) {
|
||||
let titleEl: HTMLElement;
|
||||
if(this.chat.type === 'pinned') {
|
||||
titleEl = i18n('PinnedMessagesCount', [count]);
|
||||
if(count === undefined) titleEl = i18n('Loading');
|
||||
else titleEl = i18n('PinnedMessagesCount', [count]);
|
||||
|
||||
if(count === undefined) {
|
||||
this.appMessagesManager.getSearchCounters(this.peerId, [{_: 'inputMessagesFilterPinned'}]).then(result => {
|
||||
@ -481,7 +482,8 @@ export default class ChatTopbar {
|
||||
});
|
||||
}
|
||||
} else if(this.chat.type === 'discussion') {
|
||||
titleEl = i18n('Chat.Title.Comments', [count]);
|
||||
if(count === undefined) titleEl = i18n('Loading');
|
||||
else titleEl = i18n('Chat.Title.Comments', [count]);
|
||||
|
||||
if(count === undefined) {
|
||||
Promise.all([
|
||||
|
@ -10,6 +10,7 @@ import { fastRaf } from "../../../helpers/schedulers";
|
||||
import appImManager from "../../../lib/appManagers/appImManager";
|
||||
import appStateManager from "../../../lib/appManagers/appStateManager";
|
||||
import Config from "../../../lib/config";
|
||||
import { i18n, LangPackKey } from "../../../lib/langPack";
|
||||
import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
||||
import rootScope from "../../../lib/rootScope";
|
||||
import { putPreloader } from "../../misc";
|
||||
@ -25,19 +26,32 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
private scroll: Scrollable;
|
||||
private stickyIntersector: StickyIntersector;
|
||||
|
||||
private loadedURLs: Set<string> = new Set();
|
||||
|
||||
init() {
|
||||
this.content = document.getElementById('content-emoji') as HTMLDivElement;
|
||||
|
||||
const categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"];
|
||||
const categories: LangPackKey[] = [
|
||||
'Emoji.SmilesAndPeople',
|
||||
'Emoji.AnimalsAndNature',
|
||||
'Emoji.FoodAndDrink',
|
||||
'Emoji.TravelAndPlaces',
|
||||
'Emoji.ActivityAndSport',
|
||||
'Emoji.Objects',
|
||||
/* 'Emoji.Symbols', */
|
||||
'Emoji.Flags',
|
||||
'Skin Tones' as any
|
||||
];
|
||||
const divs: {
|
||||
[category: string]: HTMLDivElement
|
||||
[category in LangPackKey]?: HTMLDivElement
|
||||
} = {};
|
||||
|
||||
const sorted: {
|
||||
[category: string]: string[]
|
||||
} = {
|
||||
'Recent': []
|
||||
};
|
||||
const sorted: Map<LangPackKey, string[]> = new Map([
|
||||
[
|
||||
'Emoji.Recent',
|
||||
[]
|
||||
]
|
||||
]);
|
||||
|
||||
for(const emoji in Config.Emoji) {
|
||||
const details = Config.Emoji[emoji];
|
||||
@ -45,32 +59,35 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
const category = categories[+i[0] - 1];
|
||||
if(!category) continue; // maybe it's skin tones
|
||||
|
||||
if(!sorted[category]) sorted[category] = [];
|
||||
sorted[category][+i.slice(1) || 0] = emoji;
|
||||
let s = sorted.get(category);
|
||||
if(!s) {
|
||||
s = [];
|
||||
sorted.set(category, s);
|
||||
}
|
||||
|
||||
s[+i.slice(1) || 0] = emoji;
|
||||
}
|
||||
|
||||
//console.log('emoticons sorted:', sorted);
|
||||
|
||||
//Object.keys(sorted).forEach(c => sorted[c].sort((a, b) => a - b));
|
||||
|
||||
categories.pop();
|
||||
delete sorted["Skin Tones"];
|
||||
sorted.delete(categories.pop());
|
||||
|
||||
//console.time('emojiParse');
|
||||
for(const category in sorted) {
|
||||
sorted.forEach((emojis, category) => {
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('emoji-category');
|
||||
|
||||
const titleDiv = document.createElement('div');
|
||||
titleDiv.classList.add('category-title');
|
||||
titleDiv.innerText = category;
|
||||
titleDiv.append(i18n(category));
|
||||
|
||||
const itemsDiv = document.createElement('div');
|
||||
itemsDiv.classList.add('category-items');
|
||||
|
||||
div.append(titleDiv, itemsDiv);
|
||||
|
||||
const emojis = sorted[category];
|
||||
emojis.forEach(emoji => {
|
||||
/* if(emojiUnicode(emoji) === '1f481-200d-2642') {
|
||||
console.log('append emoji', emoji, emojiUnicode(emoji));
|
||||
@ -86,7 +103,8 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
});
|
||||
|
||||
divs[category] = div;
|
||||
}
|
||||
});
|
||||
|
||||
//console.timeEnd('emojiParse');
|
||||
|
||||
const menu = this.content.previousElementSibling as HTMLElement;
|
||||
@ -107,14 +125,14 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
]).then(() => {
|
||||
preloader.remove();
|
||||
|
||||
this.recentItemsDiv = divs['Recent'].querySelector('.category-items');
|
||||
this.recentItemsDiv = divs['Emoji.Recent'].querySelector('.category-items');
|
||||
for(const emoji of this.recent) {
|
||||
this.appendEmoji(emoji, this.recentItemsDiv);
|
||||
}
|
||||
|
||||
this.recentItemsDiv.parentElement.classList.toggle('hide', !this.recent.length);
|
||||
|
||||
categories.unshift('Recent');
|
||||
categories.unshift('Emoji.Recent');
|
||||
categories.map(category => {
|
||||
const div = divs[category];
|
||||
|
||||
@ -173,27 +191,32 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
if(spanEmoji.firstElementChild && !RichTextProcessor.emojiSupported) {
|
||||
const image = spanEmoji.firstElementChild as HTMLImageElement;
|
||||
image.setAttribute('loading', 'lazy');
|
||||
|
||||
const placeholder = document.createElement('span');
|
||||
placeholder.classList.add('emoji-placeholder');
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '0';
|
||||
placeholder.style.opacity = '1';
|
||||
const url = image.src;
|
||||
if(!this.loadedURLs.has(url)) {
|
||||
const placeholder = document.createElement('span');
|
||||
placeholder.classList.add('emoji-placeholder');
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '0';
|
||||
placeholder.style.opacity = '1';
|
||||
}
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
fastRaf(() => {
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '';
|
||||
placeholder.style.opacity = '';
|
||||
}
|
||||
|
||||
spanEmoji.classList.remove('empty');
|
||||
|
||||
this.loadedURLs.add(url);
|
||||
});
|
||||
}, {once: true});
|
||||
|
||||
spanEmoji.append(placeholder);
|
||||
}
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
fastRaf(() => {
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '';
|
||||
placeholder.style.opacity = '';
|
||||
}
|
||||
|
||||
spanEmoji.classList.remove('empty');
|
||||
});
|
||||
}, {once: true});
|
||||
|
||||
spanEmoji.append(placeholder);
|
||||
}
|
||||
|
||||
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
|
||||
@ -216,7 +239,12 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
//if(target.tagName !== 'SPAN') return;
|
||||
|
||||
if(target.tagName === 'SPAN' && !target.classList.contains('emoji')) {
|
||||
target = findUpClassName(target, 'category-item').firstChild as HTMLElement;
|
||||
target = findUpClassName(target, 'category-item');
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
target = target.firstChild as HTMLElement;
|
||||
} else if(target.tagName === 'DIV') return;
|
||||
|
||||
//console.log('contentEmoji div', target);
|
||||
@ -253,4 +281,4 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
onClose() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import type Chat from "../chat/chat";
|
||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||
import { calcImageInBox, placeCaretAtEnd, isSendShortcutPressed } from "../../helpers/dom";
|
||||
import InputField from "../inputField";
|
||||
import PopupElement from ".";
|
||||
@ -14,7 +13,7 @@ import { toast } from "../toast";
|
||||
import { prepareAlbum, wrapDocument } from "../wrappers";
|
||||
import CheckboxField from "../checkboxField";
|
||||
import SendContextMenu from "../chat/sendContextMenu";
|
||||
import { createPosterForVideo, createPosterFromVideo, onVideoLoad } from "../../helpers/files";
|
||||
import { createPosterFromVideo, onVideoLoad } from "../../helpers/files";
|
||||
import { MyDocument } from "../../lib/appManagers/appDocsManager";
|
||||
import I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
||||
|
||||
@ -50,7 +49,7 @@ export default class PopupNewMedia extends PopupElement {
|
||||
inputField: InputField;
|
||||
|
||||
constructor(private chat: Chat, files: File[], willAttachType: PopupNewMedia['willAttach']['type']) {
|
||||
super('popup-send-photo popup-new-media', null, {closable: true, withConfirm: 'PreviewSender.Send'});
|
||||
super('popup-send-photo popup-new-media', null, {closable: true, withConfirm: 'Modal.Send'});
|
||||
|
||||
this.willAttach.type = willAttachType;
|
||||
|
||||
|
@ -87,28 +87,18 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
|
||||
} */
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
let rect = r.getBoundingClientRect();
|
||||
const rect = r.getBoundingClientRect();
|
||||
elem.classList.add('c-ripple__circle');
|
||||
|
||||
let clickX = clientX - rect.left;
|
||||
let clickY = clientY - rect.top;
|
||||
const clickX = clientX - rect.left;
|
||||
const clickY = clientY - rect.top;
|
||||
|
||||
let size: number, clickPos: number;
|
||||
if(rect.width > rect.height) {
|
||||
size = rect.width;
|
||||
clickPos = clickX;
|
||||
} else {
|
||||
size = rect.height;
|
||||
clickPos = clickY;
|
||||
}
|
||||
|
||||
let offsetFromCenter = clickPos > (size / 2) ? size - clickPos : clickPos;
|
||||
size = size - offsetFromCenter;
|
||||
size *= 1.1;
|
||||
const radius = Math.sqrt((Math.abs(clickY - rect.height / 2) + rect.height / 2) ** 2 + (Math.abs(clickX - rect.width / 2) + rect.width / 2) ** 2);
|
||||
const size = radius;
|
||||
|
||||
// center of circle
|
||||
let x = clickX - size / 2;
|
||||
let y = clickY - size / 2;
|
||||
const x = clickX - size / 2;
|
||||
const y = clickY - size / 2;
|
||||
|
||||
//console.log('ripple click', offsetFromCenter, size, clickX, clickY);
|
||||
|
||||
|
@ -31,7 +31,7 @@ export default class Row {
|
||||
noCheckboxSubtitle: boolean,
|
||||
title: string,
|
||||
titleLangKey: LangPackKey,
|
||||
titleRight: string,
|
||||
titleRight: string | HTMLElement,
|
||||
clickable: boolean | ((e: Event) => void),
|
||||
navigationTab: SliderSuperTab
|
||||
}> = {}) {
|
||||
@ -45,6 +45,7 @@ export default class Row {
|
||||
} else if(options.subtitleLangKey) {
|
||||
this.subtitle.append(i18n(options.subtitleLangKey));
|
||||
}
|
||||
this.container.append(this.subtitle);
|
||||
|
||||
let havePadding = false;
|
||||
if(options.radioField || options.checkboxField) {
|
||||
@ -56,9 +57,16 @@ export default class Row {
|
||||
|
||||
if(options.checkboxField) {
|
||||
this.checkboxField = options.checkboxField;
|
||||
this.container.append(this.checkboxField.label);
|
||||
|
||||
const isToggle = options.checkboxField.label.classList.contains('checkbox-field-toggle');
|
||||
if(isToggle) {
|
||||
this.container.classList.add('row-with-toggle');
|
||||
options.titleRight = this.checkboxField.label;
|
||||
} else {
|
||||
this.container.append(this.checkboxField.label);
|
||||
}
|
||||
|
||||
if(!options.noCheckboxSubtitle) {
|
||||
if(!options.noCheckboxSubtitle && !isToggle) {
|
||||
this.checkboxField.input.addEventListener('change', () => {
|
||||
replaceContent(this.subtitle, i18n(this.checkboxField.input.checked ? 'Checkbox.Enabled' : 'Checkbox.Disabled'));
|
||||
});
|
||||
@ -67,41 +75,47 @@ export default class Row {
|
||||
|
||||
const i = options.radioField || options.checkboxField;
|
||||
i.label.classList.add('disable-hover');
|
||||
} else {
|
||||
if(options.title || options.titleLangKey) {
|
||||
let c: HTMLElement;
|
||||
if(options.titleRight) {
|
||||
c = document.createElement('div');
|
||||
c.classList.add('row-title-row');
|
||||
this.container.append(c);
|
||||
} else {
|
||||
c = this.container;
|
||||
}
|
||||
}
|
||||
|
||||
if(options.title || options.titleLangKey) {
|
||||
let c: HTMLElement;
|
||||
if(options.titleRight) {
|
||||
c = document.createElement('div');
|
||||
c.classList.add('row-title-row');
|
||||
this.container.append(c);
|
||||
} else {
|
||||
c = this.container;
|
||||
}
|
||||
|
||||
this.title = document.createElement('div');
|
||||
this.title.classList.add('row-title');
|
||||
if(options.title) {
|
||||
this.title.innerHTML = options.title;
|
||||
} else {
|
||||
this.title.append(i18n(options.titleLangKey));
|
||||
}
|
||||
c.append(this.title);
|
||||
this.title = document.createElement('div');
|
||||
this.title.classList.add('row-title');
|
||||
if(options.title) {
|
||||
this.title.innerHTML = options.title;
|
||||
} else {
|
||||
this.title.append(i18n(options.titleLangKey));
|
||||
}
|
||||
c.append(this.title);
|
||||
|
||||
if(options.titleRight) {
|
||||
const titleRight = document.createElement('div');
|
||||
titleRight.classList.add('row-title', 'row-title-right');
|
||||
if(options.titleRight) {
|
||||
const titleRight = document.createElement('div');
|
||||
titleRight.classList.add('row-title', 'row-title-right');
|
||||
|
||||
if(typeof(options.titleRight) === 'string') {
|
||||
titleRight.innerHTML = options.titleRight;
|
||||
c.append(titleRight);
|
||||
} else {
|
||||
titleRight.append(options.titleRight);
|
||||
}
|
||||
}
|
||||
|
||||
if(options.icon) {
|
||||
havePadding = true;
|
||||
this.title.classList.add('tgico', 'tgico-' + options.icon);
|
||||
this.container.classList.add('row-with-icon');
|
||||
c.append(titleRight);
|
||||
}
|
||||
}
|
||||
|
||||
if(options.icon) {
|
||||
havePadding = true;
|
||||
this.title.classList.add('tgico', 'tgico-' + options.icon);
|
||||
this.container.classList.add('row-with-icon');
|
||||
}
|
||||
|
||||
if(havePadding) {
|
||||
this.container.classList.add('row-with-padding');
|
||||
}
|
||||
@ -125,8 +139,6 @@ export default class Row {
|
||||
this.container.prepend(this.container.lastElementChild);
|
||||
} */
|
||||
}
|
||||
|
||||
this.container.append(this.subtitle);
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { isTouchSupported } from "../helpers/touchSupport";
|
||||
import { logger, LogLevels } from "../lib/logger";
|
||||
import fastSmoothScroll, { FocusDirection } from "../helpers/fastSmoothScroll";
|
||||
import useHeavyAnimationCheck from "../hooks/useHeavyAnimationCheck";
|
||||
import { cancelEvent } from "../helpers/dom";
|
||||
/*
|
||||
var el = $0;
|
||||
var height = 0;
|
||||
@ -250,10 +251,11 @@ export class ScrollableX extends ScrollableBase {
|
||||
const scrollHorizontally = (e: any) => {
|
||||
if(!e.deltaX) {
|
||||
this.container!.scrollLeft += e.deltaY / 4;
|
||||
cancelEvent(e);
|
||||
}
|
||||
};
|
||||
|
||||
this.container.addEventListener('wheel', scrollHorizontally, {passive: true});
|
||||
this.container.addEventListener('wheel', scrollHorizontally, {passive: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
|
||||
import rootScope from "../../../lib/rootScope";
|
||||
import InputSearch from "../../inputSearch";
|
||||
import { canFocus } from "../../../helpers/dom";
|
||||
import { isMobile } from "../../../helpers/userAgent";
|
||||
|
||||
// TODO: поиск по людям глобальный, если не нашло в контактах никого
|
||||
|
||||
@ -53,7 +54,7 @@ export default class AppContactsTab extends SliderSuperTab {
|
||||
}
|
||||
|
||||
onOpenAfterTimeout() {
|
||||
if(!canFocus(true)) return;
|
||||
if(isMobile || !canFocus(true)) return;
|
||||
this.inputSearch.input.focus();
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,13 @@
|
||||
import { SettingSection } from "..";
|
||||
import { randomLong } from "../../../helpers/random";
|
||||
import I18n from "../../../lib/langPack";
|
||||
import apiManager from "../../../lib/mtproto/mtprotoworker";
|
||||
import RadioField from "../../radioField";
|
||||
import Row, { RadioFormFromRows } from "../../row";
|
||||
import { SliderSuperTab } from "../../slider"
|
||||
|
||||
export default class AppLanguageTab extends SliderSuperTab {
|
||||
protected init() {
|
||||
protected async init() {
|
||||
this.container.classList.add('language-container');
|
||||
this.setTitle('Telegram.LanguageViewController');
|
||||
|
||||
@ -20,94 +21,42 @@ export default class AppLanguageTab extends SliderSuperTab {
|
||||
|
||||
const radioRows: Map<string, Row> = new Map();
|
||||
|
||||
let r = [{
|
||||
code: 'en',
|
||||
text: 'English',
|
||||
subtitle: 'English'
|
||||
}, {
|
||||
code: 'be',
|
||||
text: 'Belarusian',
|
||||
subtitle: 'Беларуская'
|
||||
}, {
|
||||
code: 'ca',
|
||||
text: 'Catalan',
|
||||
subtitle: 'Català'
|
||||
}, {
|
||||
code: 'nl',
|
||||
text: 'Dutch',
|
||||
subtitle: 'Nederlands'
|
||||
}, {
|
||||
code: 'fr',
|
||||
text: 'French',
|
||||
subtitle: 'Français'
|
||||
}, {
|
||||
code: 'de',
|
||||
text: 'German',
|
||||
subtitle: 'Deutsch'
|
||||
}, {
|
||||
code: 'it',
|
||||
text: 'Italian',
|
||||
subtitle: 'Italiano'
|
||||
}, {
|
||||
code: 'ms',
|
||||
text: 'Malay',
|
||||
subtitle: 'Bahasa Melayu'
|
||||
}, {
|
||||
code: 'pl',
|
||||
text: 'Polish',
|
||||
subtitle: 'Polski'
|
||||
}, {
|
||||
code: 'pt',
|
||||
text: 'Portuguese (Brazil)',
|
||||
subtitle: 'Português (Brasil)'
|
||||
}, {
|
||||
code: 'ru',
|
||||
text: 'Russian',
|
||||
subtitle: 'Русский'
|
||||
}, {
|
||||
code: 'es',
|
||||
text: 'Spanish',
|
||||
subtitle: 'Español'
|
||||
}, {
|
||||
code: 'tr',
|
||||
text: 'Turkish',
|
||||
subtitle: 'Türkçe'
|
||||
}, {
|
||||
code: 'uk',
|
||||
text: 'Ukrainian',
|
||||
subtitle: 'Українська'
|
||||
}];
|
||||
|
||||
const random = randomLong();
|
||||
r.forEach(({code, text, subtitle}) => {
|
||||
const row = new Row({
|
||||
radioField: new RadioField({
|
||||
text,
|
||||
name: random,
|
||||
value: code
|
||||
}),
|
||||
subtitle
|
||||
const promise = apiManager.invokeApiCacheable('langpack.getLanguages', {
|
||||
lang_pack: 'macos'
|
||||
}).then((languages) => {
|
||||
const random = randomLong();
|
||||
languages.forEach((language) => {
|
||||
const row = new Row({
|
||||
radioField: new RadioField({
|
||||
text: language.name,
|
||||
name: random,
|
||||
value: language.lang_code
|
||||
}),
|
||||
subtitle: language.native_name
|
||||
});
|
||||
|
||||
radioRows.set(language.lang_code, row);
|
||||
});
|
||||
|
||||
radioRows.set(code, row);
|
||||
|
||||
const form = RadioFormFromRows([...radioRows.values()], (value) => {
|
||||
I18n.getLangPack(value);
|
||||
});
|
||||
|
||||
I18n.getCacheLangPack().then(langPack => {
|
||||
const row = radioRows.get(langPack.lang_code);
|
||||
if(!row) {
|
||||
console.error('no row', row, langPack);
|
||||
return;
|
||||
}
|
||||
|
||||
row.radioField.setValueSilently(true);
|
||||
});
|
||||
|
||||
section.content.append(form);
|
||||
});
|
||||
|
||||
const form = RadioFormFromRows([...radioRows.values()], (value) => {
|
||||
I18n.getLangPack(value);
|
||||
});
|
||||
|
||||
I18n.getCacheLangPack().then(langPack => {
|
||||
const row = radioRows.get(langPack.lang_code);
|
||||
if(!row) {
|
||||
console.error('no row', row, langPack);
|
||||
return;
|
||||
}
|
||||
|
||||
row.radioField.setValueSilently(true);
|
||||
});
|
||||
|
||||
section.content.append(form);
|
||||
|
||||
this.scrollable.append(section.container);
|
||||
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
@ -5,18 +5,17 @@
|
||||
*/
|
||||
|
||||
import appImManager from "../../../lib/appManagers/appImManager";
|
||||
import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
|
||||
import appMessagesManager, { AppMessagesManager } from "../../../lib/appManagers/appMessagesManager";
|
||||
import appPeersManager from "../../../lib/appManagers/appPeersManager";
|
||||
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
||||
import appUsersManager, { User } from "../../../lib/appManagers/appUsersManager";
|
||||
import { logger } from "../../../lib/logger";
|
||||
import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
||||
import rootScope from "../../../lib/rootScope";
|
||||
import AppSearchSuper, { SearchSuperType } from "../../appSearchSuper.";
|
||||
import AvatarElement from "../../avatar";
|
||||
import AvatarElement, { openAvatarViewer } from "../../avatar";
|
||||
import SidebarSlider, { SliderSuperTab } from "../../slider";
|
||||
import CheckboxField from "../../checkboxField";
|
||||
import { attachClickEvent, replaceContent, whichChild } from "../../../helpers/dom";
|
||||
import { attachClickEvent, replaceContent, cancelEvent } from "../../../helpers/dom";
|
||||
import appSidebarRight from "..";
|
||||
import { TransitionSlider } from "../../transition";
|
||||
import appNotificationsManager from "../../../lib/appManagers/appNotificationsManager";
|
||||
@ -25,7 +24,7 @@ import PeerTitle from "../../peerTitle";
|
||||
import AppEditChannelTab from "./editChannel";
|
||||
import AppEditContactTab from "./editContact";
|
||||
import appChatsManager, { Channel } from "../../../lib/appManagers/appChatsManager";
|
||||
import { Chat, UserProfilePhoto } from "../../../layer";
|
||||
import { Chat, Message, MessageAction, ChatFull, Photo } from "../../../layer";
|
||||
import Button from "../../button";
|
||||
import ButtonIcon from "../../buttonIcon";
|
||||
import I18n, { i18n, LangPackKey } from "../../../lib/langPack";
|
||||
@ -43,6 +42,8 @@ import { MOUNT_CLASS_TO } from "../../../config/debug";
|
||||
import AppAddMembersTab from "../../sidebarLeft/tabs/addMembers";
|
||||
import PopupPickUser from "../../popups/pickUser";
|
||||
import PopupPeer from "../../popups/peer";
|
||||
import Scrollable from "../../scrollable";
|
||||
import { isTouchSupported } from "../../../helpers/touchSupport";
|
||||
|
||||
let setText = (text: string, row: Row) => {
|
||||
fastRaf(() => {
|
||||
@ -166,21 +167,21 @@ class PeerProfileAvatars {
|
||||
public static BASE_CLASS = 'profile-avatars';
|
||||
public container: HTMLElement;
|
||||
public avatars: HTMLElement;
|
||||
//public gradient: HTMLElement;
|
||||
public gradient: HTMLElement;
|
||||
public info: HTMLElement;
|
||||
public tabs: HTMLDivElement;
|
||||
public listLoader: ListLoader<string>;
|
||||
public listLoader: ListLoader<string | Message.messageService>;
|
||||
public peerId: number;
|
||||
|
||||
constructor() {
|
||||
constructor(public scrollable: Scrollable) {
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add(PeerProfileAvatars.BASE_CLASS + '-container');
|
||||
|
||||
this.avatars = document.createElement('div');
|
||||
this.avatars.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatars');
|
||||
|
||||
//this.gradient = document.createElement('div');
|
||||
//this.gradient.classList.add(PeerProfileAvatars.BASE_CLASS + '-gradient');
|
||||
this.gradient = document.createElement('div');
|
||||
this.gradient.classList.add(PeerProfileAvatars.BASE_CLASS + '-gradient');
|
||||
|
||||
this.info = document.createElement('div');
|
||||
this.info.classList.add(PeerProfileAvatars.BASE_CLASS + '-info');
|
||||
@ -188,29 +189,77 @@ class PeerProfileAvatars {
|
||||
this.tabs = document.createElement('div');
|
||||
this.tabs.classList.add(PeerProfileAvatars.BASE_CLASS + '-tabs');
|
||||
|
||||
this.container.append(this.avatars, /* this.gradient, */this.info, this.tabs);
|
||||
this.container.append(this.avatars, this.gradient, this.info, this.tabs);
|
||||
|
||||
const checkScrollTop = () => {
|
||||
if(this.scrollable.scrollTop !== 0) {
|
||||
this.scrollable.scrollIntoViewNew(this.scrollable.container.firstElementChild as HTMLElement, 'start');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const SWITCH_ZONE = 1 / 3;
|
||||
let cancel = false;
|
||||
attachClickEvent(this.container, (_e) => {
|
||||
let freeze = false;
|
||||
attachClickEvent(this.container, async(_e) => {
|
||||
if(freeze) {
|
||||
cancelEvent(_e);
|
||||
return;
|
||||
}
|
||||
|
||||
if(cancel) {
|
||||
cancel = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!checkScrollTop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = this.container.getBoundingClientRect();
|
||||
|
||||
const e = (_e as TouchEvent).touches ? (_e as TouchEvent).touches[0] : _e as MouseEvent;
|
||||
const x = e.pageX;
|
||||
|
||||
const centerX = rect.right - (rect.width / 2);
|
||||
const toRight = x > centerX;
|
||||
const clickX = x - rect.left;
|
||||
if(clickX > (rect.width * SWITCH_ZONE) && clickX < (rect.width - rect.width * SWITCH_ZONE)) {
|
||||
const peerId = this.peerId;
|
||||
|
||||
// this.avatars.classList.remove('no-transition');
|
||||
// fastRaf(() => {
|
||||
this.listLoader.go(toRight ? 1 : -1);
|
||||
// });
|
||||
const targets: {element: HTMLElement, item: string | Message.messageService}[] = [];
|
||||
this.listLoader.previous.concat(this.listLoader.current, this.listLoader.next).forEach((item, idx) => {
|
||||
targets.push({
|
||||
element: /* null */this.avatars.children[idx] as HTMLElement,
|
||||
item
|
||||
});
|
||||
});
|
||||
|
||||
const prevTargets = targets.slice(0, this.listLoader.previous.length);
|
||||
const nextTargets = targets.slice(this.listLoader.previous.length + 1);
|
||||
|
||||
const target = this.avatars.children[this.listLoader.previous.length] as HTMLElement;
|
||||
freeze = true;
|
||||
openAvatarViewer(target, peerId, () => peerId === this.peerId, this.listLoader.current, prevTargets, nextTargets);
|
||||
freeze = false;
|
||||
} else {
|
||||
const centerX = rect.right - (rect.width / 2);
|
||||
const toRight = x > centerX;
|
||||
|
||||
// this.avatars.classList.remove('no-transition');
|
||||
// fastRaf(() => {
|
||||
this.listLoader.go(toRight ? 1 : -1);
|
||||
// });
|
||||
}
|
||||
});
|
||||
|
||||
const cancelNextClick = () => {
|
||||
cancel = true;
|
||||
document.body.addEventListener(isTouchSupported ? 'touchend' : 'click', (e) => {
|
||||
cancel = false;
|
||||
}, {once: true});
|
||||
};
|
||||
|
||||
let width = 0, x = 0, lastDiffX = 0, lastIndex = 0, minX = 0;
|
||||
const swipeHandler = new SwipeHandler({
|
||||
element: this.avatars,
|
||||
@ -225,7 +274,11 @@ class PeerProfileAvatars {
|
||||
return false;
|
||||
},
|
||||
verifyTouchTarget: (e) => {
|
||||
if(this.tabs.classList.contains('hide')) {
|
||||
if(!checkScrollTop()) {
|
||||
cancelNextClick();
|
||||
cancelEvent(e);
|
||||
return false;
|
||||
} else if(this.tabs.classList.contains('hide') || freeze) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -247,7 +300,7 @@ class PeerProfileAvatars {
|
||||
},
|
||||
onReset: () => {
|
||||
const addIndex = Math.ceil(Math.abs(lastDiffX) / (width / 2)) * (lastDiffX >= 0 ? 1 : -1);
|
||||
cancel = true;
|
||||
cancelNextClick();
|
||||
|
||||
//console.log(addIndex);
|
||||
|
||||
@ -268,15 +321,51 @@ class PeerProfileAvatars {
|
||||
}
|
||||
|
||||
const loadCount = 50;
|
||||
const listLoader: PeerProfileAvatars['listLoader'] = this.listLoader = new ListLoader<string>({
|
||||
const listLoader: PeerProfileAvatars['listLoader'] = this.listLoader = new ListLoader<string | Message.messageService>({
|
||||
loadCount,
|
||||
loadMore: (anchor, older) => {
|
||||
return appPhotosManager.getUserPhotos(peerId, anchor || listLoader.current, loadCount).then(result => {
|
||||
return {
|
||||
count: result.count,
|
||||
items: result.photos
|
||||
};
|
||||
});
|
||||
if(peerId > 0) {
|
||||
return appPhotosManager.getUserPhotos(peerId, (anchor || listLoader.current) as any, loadCount).then(result => {
|
||||
return {
|
||||
count: result.count,
|
||||
items: result.photos
|
||||
};
|
||||
});
|
||||
} else {
|
||||
const promises: [Promise<ChatFull>, ReturnType<AppMessagesManager['getSearch']>] = [] as any;
|
||||
if(!listLoader.current) {
|
||||
promises.push(appProfileManager.getChatFull(peerId));
|
||||
}
|
||||
|
||||
promises.push(appMessagesManager.getSearch({
|
||||
peerId,
|
||||
maxId: Number.MAX_SAFE_INTEGER,
|
||||
inputFilter: {
|
||||
_: 'inputMessagesFilterChatPhotos'
|
||||
},
|
||||
limit: loadCount,
|
||||
backLimit: 0
|
||||
}));
|
||||
|
||||
return Promise.all(promises).then((result) => {
|
||||
const value = result.pop() as typeof result[1];
|
||||
|
||||
if(!listLoader.current) {
|
||||
const chatFull = result[0];
|
||||
const message = value.history.findAndSplice(m => {
|
||||
return ((m as Message.messageService).action as MessageAction.messageActionChannelEditPhoto).photo.id === chatFull.chat_photo.id;
|
||||
}) as Message.messageService;
|
||||
|
||||
listLoader.current = message || appMessagesManager.generateFakeAvatarMessage(this.peerId, chatFull.chat_photo);
|
||||
}
|
||||
|
||||
//console.log('avatars loaded:', value);
|
||||
return {
|
||||
count: value.count,
|
||||
items: value.history
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
processItem: this.processItem,
|
||||
onJump: (item, older) => {
|
||||
@ -292,7 +381,10 @@ class PeerProfileAvatars {
|
||||
}
|
||||
});
|
||||
|
||||
listLoader.current = (photo as UserProfilePhoto.userProfilePhoto).photo_id;
|
||||
if(photo._ === 'userProfilePhoto') {
|
||||
listLoader.current = photo.photo_id;
|
||||
}
|
||||
|
||||
this.processItem(listLoader.current);
|
||||
|
||||
listLoader.load(true);
|
||||
@ -310,11 +402,17 @@ class PeerProfileAvatars {
|
||||
this.tabs.classList.toggle('hide', this.tabs.childElementCount <= 1);
|
||||
}
|
||||
|
||||
public processItem = (photoId: string) => {
|
||||
public processItem = (photoId: string | Message.messageService) => {
|
||||
const avatar = document.createElement('div');
|
||||
avatar.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar');
|
||||
|
||||
const photo = appPhotosManager.getPhoto(photoId);
|
||||
let photo: Photo.photo;
|
||||
if(photoId) {
|
||||
photo = typeof(photoId) === 'string' ?
|
||||
appPhotosManager.getPhoto(photoId) :
|
||||
(photoId.action as MessageAction.messageActionChannelEditPhoto).photo as Photo.photo;
|
||||
}
|
||||
|
||||
const img = new Image();
|
||||
img.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar-image');
|
||||
img.draggable = false;
|
||||
@ -357,6 +455,10 @@ class PeerProfile {
|
||||
private peerId = 0;
|
||||
private threadId: number;
|
||||
|
||||
constructor(public scrollable: Scrollable) {
|
||||
|
||||
}
|
||||
|
||||
public init() {
|
||||
this.init = null;
|
||||
|
||||
@ -419,11 +521,17 @@ class PeerProfile {
|
||||
});
|
||||
|
||||
this.notifications = new Row({
|
||||
checkboxField: new CheckboxField({text: 'Notifications'})
|
||||
checkboxField: new CheckboxField({toggle: true}),
|
||||
titleLangKey: 'Notifications',
|
||||
icon: 'unmute'
|
||||
});
|
||||
|
||||
this.section.content.append(this.phone.container, this.username.container, this.bio.container, this.notifications.container);
|
||||
this.element.append(this.section.container);
|
||||
|
||||
const delimiter = document.createElement('div');
|
||||
delimiter.classList.add('gradient-delimiter');
|
||||
|
||||
this.element.append(this.section.container, delimiter);
|
||||
|
||||
this.notifications.checkboxField.input.addEventListener('change', (e) => {
|
||||
if(!e.isTrusted) {
|
||||
@ -502,7 +610,7 @@ class PeerProfile {
|
||||
|
||||
if(photo) {
|
||||
const oldAvatars = this.avatars;
|
||||
this.avatars = new PeerProfileAvatars();
|
||||
this.avatars = new PeerProfileAvatars(this.scrollable);
|
||||
this.avatars.setPeer(this.peerId);
|
||||
this.avatars.info.append(this.name, this.subtitle);
|
||||
|
||||
@ -511,10 +619,14 @@ class PeerProfile {
|
||||
if(oldAvatars) oldAvatars.container.replaceWith(this.avatars.container);
|
||||
else this.element.prepend(this.avatars.container);
|
||||
|
||||
this.scrollable.container.classList.add('parallax');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.scrollable.container.classList.remove('parallax');
|
||||
|
||||
if(this.avatars) {
|
||||
this.avatars.container.remove();
|
||||
this.avatars = undefined;
|
||||
@ -699,7 +811,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
|
||||
// * body
|
||||
|
||||
this.profile = new PeerProfile();
|
||||
this.profile = new PeerProfile(this.scrollable);
|
||||
this.profile.init();
|
||||
|
||||
this.scrollable.append(this.profile.element);
|
||||
@ -712,6 +824,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
||||
const top = rect.top;
|
||||
const isSharedMedia = top <= HEADER_HEIGHT;
|
||||
animatedCloseIcon.classList.toggle('state-back', isSharedMedia);
|
||||
this.searchSuper.container.classList.toggle('is-full-viewport', isSharedMedia);
|
||||
transition(+isSharedMedia);
|
||||
|
||||
if(!isSharedMedia) {
|
||||
|
@ -1,8 +1,14 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager";
|
||||
import { isInDOM, positionElementByIndex, replaceContent } from "../helpers/dom";
|
||||
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
||||
import appUsersManager from "../lib/appManagers/appUsersManager";
|
||||
import { insertInDescendSortedArray, forEachReverse } from "../helpers/array";
|
||||
import { insertInDescendSortedArray } from "../helpers/array";
|
||||
|
||||
type SortedUser = {
|
||||
peerId: number,
|
||||
|
@ -17,7 +17,7 @@ const attachGlobalListenerTo = window;
|
||||
export default class SwipeHandler {
|
||||
private element: HTMLElement;
|
||||
private onSwipe: (xDiff: number, yDiff: number) => boolean;
|
||||
private verifyTouchTarget: (evt: Touch | MouseEvent) => boolean;
|
||||
private verifyTouchTarget: (evt: TouchEvent | MouseEvent) => boolean;
|
||||
private onFirstSwipe: () => void;
|
||||
private onReset: () => void;
|
||||
|
||||
@ -65,7 +65,7 @@ export default class SwipeHandler {
|
||||
|
||||
handleStart = (_e: TouchEvent | MouseEvent) => {
|
||||
const e = getEvent(_e);
|
||||
if(this.verifyTouchTarget && !this.verifyTouchTarget(e)) {
|
||||
if(this.verifyTouchTarget && !this.verifyTouchTarget(_e)) {
|
||||
return this.reset();
|
||||
}
|
||||
|
||||
|
@ -649,7 +649,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
||||
}) {
|
||||
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) {
|
||||
if(boxWidth && boxHeight && photo._ === 'document') {
|
||||
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight);
|
||||
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -677,7 +677,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
||||
image = new Image();
|
||||
|
||||
if(boxWidth && boxHeight) { // !album
|
||||
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight);
|
||||
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message);
|
||||
}
|
||||
|
||||
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo);
|
||||
|
@ -13,7 +13,7 @@ const App = {
|
||||
id: 1025907,
|
||||
hash: '452b0359b988148995f22ff0f4229750',
|
||||
version: '0.4.0',
|
||||
langPackVersion: '0.1.1',
|
||||
langPackVersion: '0.1.2',
|
||||
langPack: 'macos',
|
||||
langPackCode: 'en',
|
||||
domains: [] as string[],
|
||||
|
50
src/helpers/dom/getVisibleRect.ts
Normal file
50
src/helpers/dom/getVisibleRect.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
export default function getVisibleRect(element: HTMLElement, overflowElement: HTMLElement) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const overflowRect = overflowElement.getBoundingClientRect();
|
||||
|
||||
let {top: overflowTop, bottom: overflowBottom} = overflowRect;
|
||||
|
||||
// * respect sticky headers
|
||||
const sticky = overflowElement.querySelector('.sticky');
|
||||
if(sticky) {
|
||||
const stickyRect = sticky.getBoundingClientRect();
|
||||
overflowTop = stickyRect.bottom;
|
||||
}
|
||||
|
||||
if(rect.top >= overflowBottom
|
||||
|| rect.bottom <= overflowTop
|
||||
|| rect.right <= overflowRect.left
|
||||
|| rect.left >= overflowRect.right) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const overflow = {
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
left: false,
|
||||
vertical: 0 as 0 | 1 | 2,
|
||||
horizontal: 0 as 0 | 1 | 2
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const w: any = 'visualViewport' in window ? window.visualViewport : window;
|
||||
const windowWidth = w.width || w.innerWidth;
|
||||
const windowHeight = w.height || w.innerHeight;
|
||||
|
||||
return {
|
||||
rect: {
|
||||
top: rect.top < overflowTop && overflowTop !== 0 ? (overflow.top = true, ++overflow.vertical, overflowTop) : rect.top,
|
||||
right: 0,
|
||||
bottom: rect.bottom > overflowBottom && overflowBottom !== windowHeight ? (overflow.bottom = true, ++overflow.vertical, overflowBottom) : rect.bottom,
|
||||
left: 0
|
||||
},
|
||||
overflow
|
||||
};
|
||||
}
|
@ -37,15 +37,15 @@ class MediaSizes extends EventListenerBase<{
|
||||
private sizes: {[k in 'desktop' | 'handhelds']: Sizes} = {
|
||||
handhelds: {
|
||||
regular: {
|
||||
width: 293,
|
||||
height: 293
|
||||
width: 270,
|
||||
height: 270
|
||||
},
|
||||
webpage: {
|
||||
width: 293,
|
||||
height: 213
|
||||
width: 270,
|
||||
height: 200
|
||||
},
|
||||
album: {
|
||||
width: 293,
|
||||
width: 270,
|
||||
height: 0
|
||||
},
|
||||
esgSticker: {
|
||||
@ -55,15 +55,15 @@ class MediaSizes extends EventListenerBase<{
|
||||
},
|
||||
desktop: {
|
||||
regular: {
|
||||
width: 480,
|
||||
height: 480
|
||||
width: 400,
|
||||
height: 320
|
||||
},
|
||||
webpage: {
|
||||
width: 480,
|
||||
height: 400
|
||||
width: 400,
|
||||
height: 320
|
||||
},
|
||||
album: {
|
||||
width: 451,
|
||||
width: 420,
|
||||
height: 0
|
||||
},
|
||||
esgSticker: {
|
||||
|
15
src/index.ts
15
src/index.ts
@ -7,6 +7,7 @@
|
||||
import App from './config/app';
|
||||
import findUpClassName from './helpers/dom/findUpClassName';
|
||||
import fixSafariStickyInput from './helpers/dom/fixSafariStickyInput';
|
||||
import { isMobileSafari } from './helpers/userAgent';
|
||||
import './materialize.scss';
|
||||
import './scss/style.scss';
|
||||
import './scss/tgico.scss';
|
||||
@ -259,6 +260,20 @@ console.timeEnd('get storage1'); */
|
||||
if(authState._ !== 'authStateSignedIn'/* || 1 === 1 */) {
|
||||
console.log('Will mount auth page:', authState._, Date.now() / 1000);
|
||||
|
||||
const el = document.getElementById('auth-pages');
|
||||
if(el) {
|
||||
const scrollable = el.querySelector('.scrollable');
|
||||
if((!touchSupport.isTouchSupported || isMobileSafari)) {
|
||||
scrollable.classList.add('no-scrollbar');
|
||||
}
|
||||
|
||||
const placeholder = document.createElement('div');
|
||||
placeholder.classList.add('auth-placeholder');
|
||||
|
||||
scrollable.prepend(placeholder);
|
||||
scrollable.append(placeholder.cloneNode());
|
||||
}
|
||||
|
||||
//langPromise.then(async() => {
|
||||
switch(authState._) {
|
||||
case 'authStateSignIn':
|
||||
|
16
src/lang.ts
16
src/lang.ts
@ -2,7 +2,7 @@ const lang = {
|
||||
"Animations": "Animations",
|
||||
"AttachAlbum": "Album",
|
||||
"BlockModal.Search.Placeholder": "Block user...",
|
||||
"DarkMode": "Sith Mode",
|
||||
"DarkMode": "Dark Mode",
|
||||
"FilterIncludeExcludeInfo": "Choose chats and types of chats that will\nappear and never appear in this folder.",
|
||||
"FilterNameInputLabel": "Folder Name",
|
||||
"FilterMenuDelete": "Delete Folder",
|
||||
@ -59,7 +59,6 @@ const lang = {
|
||||
"Checkbox.Disabled": "Disabled",
|
||||
"Error.PreviewSender.CaptionTooLong": "Caption is too long.",
|
||||
"PreviewSender.GroupItems": "Group items",
|
||||
"PreviewSender.Send": "SEND",
|
||||
"PreviewSender.SendAlbum": {
|
||||
"one_value": "Send Album",
|
||||
"other_value": "Send %d Albums"
|
||||
@ -407,6 +406,9 @@ const lang = {
|
||||
"Chat.Date.ScheduledFor": "Scheduled for %@",
|
||||
//"Chat.Date.ScheduledUntilOnline": "Scheduled until online",
|
||||
"Chat.Date.ScheduledForToday": "Scheduled for today",
|
||||
"Chat.DropTitle": "Drop files here to send them",
|
||||
"Chat.DropQuickDesc": "in a quick way",
|
||||
"Chat.DropAsFilesDesc": "without compression",
|
||||
"Chat.Service.PeerJoinedTelegram": "%@ joined Telegram",
|
||||
"Chat.Service.Channel.UpdatedTitle": "Channel renamed to \"%@\"",
|
||||
"Chat.Service.Channel.UpdatedPhoto": "Channel photo updated",
|
||||
@ -487,11 +489,21 @@ const lang = {
|
||||
"EditAccount.Username": "Username",
|
||||
"EditAccount.Title": "Edit Profile",
|
||||
"EditAccount.Logout": "Log Out",
|
||||
"Emoji.Recent": "Frequently Used",
|
||||
"Emoji.SmilesAndPeople": "Smileys & People",
|
||||
"Emoji.AnimalsAndNature": "Animals & Nature",
|
||||
"Emoji.FoodAndDrink": "Food & Drink",
|
||||
"Emoji.ActivityAndSport": "Activity & Sport",
|
||||
"Emoji.TravelAndPlaces": "Travel & Places",
|
||||
"Emoji.Objects": "Objects",
|
||||
//"Emoji.Symbols": "Symbols",
|
||||
"Emoji.Flags": "Flags",
|
||||
"LastSeen.HoursAgo": {
|
||||
"one_value": "last seen %d hour ago",
|
||||
"other_value": "last seen %d hours ago"
|
||||
},
|
||||
"Login.Register.LastName.Placeholder": "Last Name",
|
||||
"Modal.Send": "Send",
|
||||
"Telegram.GeneralSettingsViewController": "General Settings",
|
||||
"Telegram.InstalledStickerPacksController": "Stickers",
|
||||
"Telegram.NotificationSettingsViewController": "Notifications",
|
||||
|
@ -7,7 +7,7 @@
|
||||
//import apiManager from '../mtproto/apiManager';
|
||||
import animationIntersector from '../../components/animationIntersector';
|
||||
import appSidebarLeft, { LEFT_COLUMN_ACTIVE_CLASSNAME } from "../../components/sidebarLeft";
|
||||
import appSidebarRight, { AppSidebarRight, RIGHT_COLUMN_ACTIVE_CLASSNAME } from '../../components/sidebarRight';
|
||||
import appSidebarRight, { RIGHT_COLUMN_ACTIVE_CLASSNAME } from '../../components/sidebarRight';
|
||||
import mediaSizes, { ScreenSize } from '../../helpers/mediaSizes';
|
||||
import { logger, LogLevels } from "../logger";
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
@ -46,6 +46,7 @@ import AppPrivateSearchTab from '../../components/sidebarRight/tabs/search';
|
||||
import { i18n } from '../langPack';
|
||||
import { SendMessageAction } from '../../layer';
|
||||
import { highlightningColor } from '../../helpers/color';
|
||||
import { getObjectKeysAndSort } from '../../helpers/object';
|
||||
|
||||
//console.log('appImManager included33!');
|
||||
|
||||
@ -54,6 +55,11 @@ appSidebarLeft; // just to include
|
||||
export const CHAT_ANIMATION_GROUP = 'chat';
|
||||
const FOCUS_EVENT_NAME = isTouchSupported ? 'touchstart' : 'mousemove';
|
||||
|
||||
export type ChatSavedPosition = {
|
||||
mids: number[],
|
||||
top: number
|
||||
};
|
||||
|
||||
export class AppImManager {
|
||||
public columnEl = document.getElementById('column-center') as HTMLDivElement;
|
||||
public chatsContainer: HTMLElement;
|
||||
@ -165,13 +171,13 @@ export class AppImManager {
|
||||
this.setInnerPeer(peerId);
|
||||
});
|
||||
|
||||
/* rootScope.on('peer_changing', (chat) => {
|
||||
rootScope.on('peer_changing', (chat) => {
|
||||
this.saveChatPosition(chat);
|
||||
});
|
||||
|
||||
sessionStorage.get('chatPositions').then((c) => {
|
||||
sessionStorage.setToCache('chatPositions', c || {});
|
||||
}); */
|
||||
});
|
||||
}
|
||||
|
||||
private onHashChange = () => {
|
||||
@ -230,28 +236,34 @@ export class AppImManager {
|
||||
});
|
||||
}
|
||||
|
||||
/* public saveChatPosition(chat: Chat) {
|
||||
const bubble = chat.bubbles.getBubbleByPoint('top');
|
||||
if(bubble) {
|
||||
const top = bubble.getBoundingClientRect().top;
|
||||
|
||||
this.log('saving position by bubble:', bubble, top);
|
||||
public saveChatPosition(chat: Chat) {
|
||||
if(!(['chat', 'discussion'] as ChatType[]).includes(chat.type) || !chat.peerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
//const bubble = chat.bubbles.getBubbleByPoint('top');
|
||||
//if(bubble) {
|
||||
//const top = bubble.getBoundingClientRect().top;
|
||||
const top = chat.bubbles.scrollable.scrollTop;
|
||||
|
||||
const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : '');
|
||||
|
||||
const chatPositions = sessionStorage.getFromCache('chatPositions');
|
||||
chatPositions[key] = {
|
||||
mid: +bubble.dataset.mid,
|
||||
mids: getObjectKeysAndSort(chat.bubbles.bubbles, 'desc'),
|
||||
top
|
||||
};
|
||||
sessionStorage.set({chatPositions});
|
||||
}
|
||||
} as ChatSavedPosition;
|
||||
|
||||
this.log('saved chat position:', chatPositions[key]);
|
||||
|
||||
sessionStorage.set({chatPositions}, true);
|
||||
//}
|
||||
}
|
||||
|
||||
public getChatSavedPosition(chat: Chat) {
|
||||
public getChatSavedPosition(chat: Chat): ChatSavedPosition {
|
||||
const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : '');
|
||||
return sessionStorage.getFromCache('chatPositions')[key];
|
||||
} */
|
||||
}
|
||||
|
||||
private setSettings = () => {
|
||||
document.documentElement.style.setProperty('--messages-text-size', rootScope.settings.messagesTextSize + 'px');
|
||||
@ -439,8 +451,8 @@ export class AppImManager {
|
||||
if(types.length || force) {
|
||||
drops.push(new ChatDragAndDrop(dropsContainer, {
|
||||
icon: 'dragfiles',
|
||||
header: 'Drop files here to send them',
|
||||
subtitle: 'without compression',
|
||||
header: 'Chat.DropTitle',
|
||||
subtitle: 'Chat.DropAsFilesDesc',
|
||||
onDrop: (e: DragEvent) => {
|
||||
toggle(e, false);
|
||||
appImManager.log('drop', e);
|
||||
@ -452,8 +464,8 @@ export class AppImManager {
|
||||
if((foundMedia && !foundDocuments) || force) {
|
||||
drops.push(new ChatDragAndDrop(dropsContainer, {
|
||||
icon: 'dragmedia',
|
||||
header: 'Drop files here to send them',
|
||||
subtitle: 'in a quick way',
|
||||
header: 'Chat.DropTitle',
|
||||
subtitle: 'Chat.DropQuickDesc',
|
||||
onDrop: (e: DragEvent) => {
|
||||
toggle(e, false);
|
||||
appImManager.log('drop', e);
|
||||
|
@ -17,7 +17,7 @@ import { createPosterForVideo } from "../../helpers/files";
|
||||
import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object";
|
||||
import { randomLong } from "../../helpers/random";
|
||||
import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/string";
|
||||
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update } from "../../layer";
|
||||
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo } from "../../layer";
|
||||
import { InvokeApiOptions } from "../../types";
|
||||
import I18n, { i18n, join, langPack, LangPackKey, _i18n } from "../langPack";
|
||||
import { logger, LogLevels } from "../logger";
|
||||
@ -1548,6 +1548,24 @@ export class AppMessagesManager {
|
||||
return fwdHeader;
|
||||
}
|
||||
|
||||
public generateFakeAvatarMessage(peerId: number, photo: Photo) {
|
||||
const maxId = Number.MAX_SAFE_INTEGER;
|
||||
const message = {
|
||||
_: 'messageService',
|
||||
action: {
|
||||
_: 'messageActionChannelEditPhoto',
|
||||
photo
|
||||
},
|
||||
mid: maxId,
|
||||
peerId,
|
||||
date: (photo as Photo.photo).date,
|
||||
fromId: peerId
|
||||
} as Message.messageService;
|
||||
|
||||
this.getMessagesStorage(peerId)[maxId] = message;
|
||||
return message;
|
||||
}
|
||||
|
||||
public setDialogTopMessage(message: MyMessage, dialog: MTDialog.dialog = this.getDialogByPeerId(message.peerId)[0]) {
|
||||
if(dialog) {
|
||||
dialog.top_message = message.mid;
|
||||
|
@ -219,7 +219,7 @@ export class AppPhotosManager {
|
||||
return {image, loadPromise};
|
||||
}
|
||||
|
||||
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, noZoom = true) {
|
||||
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, noZoom = true, hasText?: boolean) {
|
||||
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
|
||||
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
||||
|
||||
@ -233,7 +233,12 @@ export class AppPhotosManager {
|
||||
height = 'h' in photoSize ? photoSize.h : 100;
|
||||
}
|
||||
|
||||
const {w, h} = calcImageInBox(width, height, boxWidth, boxHeight, noZoom);
|
||||
let {w, h} = calcImageInBox(width, height, boxWidth, boxHeight, noZoom);
|
||||
|
||||
/* if(hasText) {
|
||||
w = Math.max(boxWidth, w);
|
||||
} */
|
||||
|
||||
if(element instanceof SVGForeignObjectElement) {
|
||||
element.setAttributeNS(null, 'width', '' + w);
|
||||
element.setAttributeNS(null, 'height', '' + h);
|
||||
@ -257,7 +262,7 @@ export class AppPhotosManager {
|
||||
}
|
||||
|
||||
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
|
||||
const thumb = sizes?.length ? sizes[0] : null;
|
||||
const thumb = sizes?.length ? sizes.find(size => size._ === 'photoStrippedSize') : null;
|
||||
if(thumb && ('bytes' in thumb)) {
|
||||
return appPhotosManager.getImageFromStrippedThumb(thumb as any);
|
||||
}
|
||||
|
@ -4,9 +4,10 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import type { ChatSavedPosition } from './appManagers/appImManager';
|
||||
import type { State } from './appManagers/appStateManager';
|
||||
import { MOUNT_CLASS_TO } from '../config/debug';
|
||||
import { LangPackDifference } from '../layer';
|
||||
import type { State } from './appManagers/appStateManager';
|
||||
import AppStorage from './storage';
|
||||
|
||||
const sessionStorage = new AppStorage<{
|
||||
@ -21,10 +22,7 @@ const sessionStorage = new AppStorage<{
|
||||
server_time_offset: number,
|
||||
|
||||
chatPositions: {
|
||||
[peerId_threadId: string]: {
|
||||
mid: number,
|
||||
top: number
|
||||
}
|
||||
[peerId_threadId: string]: ChatSavedPosition
|
||||
},
|
||||
langPack: LangPackDifference
|
||||
} & State>({
|
||||
|
@ -12,7 +12,6 @@ import apiManager from "../lib/mtproto/mtprotoworker";
|
||||
import { RichTextProcessor } from '../lib/richtextprocessor';
|
||||
import { attachClickEvent, cancelEvent, replaceContent } from "../helpers/dom";
|
||||
import Page from "./page";
|
||||
import pageAuthCode from "./pageAuthCode";
|
||||
import InputField from "../components/inputField";
|
||||
import CheckboxField from "../components/checkboxField";
|
||||
import Button from "../components/button";
|
||||
@ -341,7 +340,7 @@ let onFirstMount = () => {
|
||||
}).then((code) => {
|
||||
//console.log('got code', code);
|
||||
|
||||
pageAuthCode.mount(Object.assign(code, {phone_number: phone_number}));
|
||||
import('./pageAuthCode').then(m => m.default.mount(Object.assign(code, {phone_number: phone_number})));
|
||||
}).catch(err => {
|
||||
this.removeAttribute('disabled');
|
||||
|
||||
|
@ -9,6 +9,7 @@ import { whichChild } from "../helpers/dom";
|
||||
import lottieLoader from "../lib/lottieLoader";
|
||||
import { horizontalMenu } from "../components/horizontalMenu";
|
||||
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||
import fastSmoothScroll from "../helpers/fastSmoothScroll";
|
||||
|
||||
class PagesManager {
|
||||
private pageId = -1;
|
||||
@ -16,10 +17,12 @@ class PagesManager {
|
||||
|
||||
private selectTab: ReturnType<typeof horizontalMenu>;
|
||||
public pagesDiv: HTMLDivElement;
|
||||
public scrollableDiv: HTMLElement;
|
||||
|
||||
constructor() {
|
||||
this.pagesDiv = document.getElementById('auth-pages') as HTMLDivElement;
|
||||
this.selectTab = horizontalMenu(null, this.pagesDiv.firstElementChild.firstElementChild as HTMLDivElement, null, () => {
|
||||
this.scrollableDiv = this.pagesDiv.querySelector('.scrollable') as HTMLElement;
|
||||
this.selectTab = horizontalMenu(null, this.scrollableDiv.querySelector('.tabs-container') as HTMLDivElement, null, () => {
|
||||
if(this.page?.onShown) {
|
||||
this.page.onShown();
|
||||
}
|
||||
@ -39,7 +42,13 @@ class PagesManager {
|
||||
lottieLoader.loadLottieWorkers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.pageId = id;
|
||||
|
||||
if(this.scrollableDiv) {
|
||||
fastSmoothScroll(this.scrollableDiv, this.scrollableDiv.firstElementChild as HTMLElement, 'start');
|
||||
}
|
||||
} else {
|
||||
this.pagesDiv.style.display = 'none';
|
||||
page.pageEl.style.display = '';
|
||||
|
@ -7,8 +7,8 @@
|
||||
avatar-element {
|
||||
--size: 54px;
|
||||
--multiplier: 1;
|
||||
--color-top: var(--peer-avatar-blue-top);
|
||||
--color-bottom: var(--peer-avatar-blue-bottom);
|
||||
--color-top: var(--avatar-color-top);
|
||||
--color-bottom: var(--avatar-color-bottom);
|
||||
color: #fff;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
@ -52,6 +52,11 @@ avatar-element {
|
||||
--color-bottom: var(--peer-avatar-pink-bottom);
|
||||
}
|
||||
|
||||
&[data-color="blue"] {
|
||||
--color-top: var(--peer-avatar-blue-top);
|
||||
--color-bottom: var(--peer-avatar-blue-bottom);
|
||||
}
|
||||
|
||||
&.tgico:before {
|
||||
font-size: calc(32px / var(--multiplier));
|
||||
}
|
||||
|
@ -195,49 +195,6 @@
|
||||
&-text {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
--size: 20px;
|
||||
margin: 0 .3125rem;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
|
||||
.checkbox-toggle {
|
||||
--toggle-width: 1.9375rem;
|
||||
width: var(--toggle-width);
|
||||
height: .875rem;
|
||||
background-color: var(--secondary-color);
|
||||
border-radius: $border-radius-big;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: background-color .2s;
|
||||
|
||||
&:before {
|
||||
--offset: 3px;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border: 2px solid var(--secondary-color);
|
||||
transition: border-color .2s, transform .2s;
|
||||
background-color: var(--surface-color);
|
||||
content: " ";
|
||||
transform: translateX(calc(var(--offset) * -1));
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + .checkbox-toggle {
|
||||
background-color: var(--primary-color);
|
||||
|
||||
&:before {
|
||||
border-color: var(--primary-color);
|
||||
transform: translateX(calc(var(--toggle-width) - 1.25rem + var(--offset)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* &-overlay {
|
||||
|
@ -833,7 +833,7 @@ $chat-helper-size: 39px;
|
||||
.btn-menu {
|
||||
padding: 8px 0;
|
||||
right: -11px;
|
||||
bottom: calc(100% + 16px);
|
||||
bottom: calc(100% + 1.25rem);
|
||||
|
||||
> div {
|
||||
padding: 0 38px 0 16px;
|
||||
@ -980,7 +980,7 @@ $chat-helper-size: 39px;
|
||||
// }
|
||||
// }
|
||||
|
||||
&.is-chat-input-hidden.is-selecting:not(.backwards) {
|
||||
.chat.type-chat &.is-chat-input-hidden.is-selecting:not(.backwards) {
|
||||
--translateY: -78px;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
@ -1130,7 +1130,7 @@ $chat-helper-size: 39px;
|
||||
}
|
||||
}
|
||||
|
||||
.bubbles.is-chat-input-hidden & {
|
||||
.chat.type-chat .bubbles.is-chat-input-hidden & {
|
||||
margin-bottom: 1rem; // .25rem is eaten by the last bubble's margin-bottom
|
||||
}
|
||||
|
||||
|
@ -321,6 +321,17 @@ $bubble-margin: .25rem;
|
||||
//background: rgba(0, 0, 0, .16);
|
||||
background: var(--message-highlightning-color);
|
||||
cursor: pointer;
|
||||
|
||||
html.no-touch & {
|
||||
opacity: 0;
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
.bubble-beside-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.forward {
|
||||
|
@ -89,7 +89,6 @@
|
||||
font-size: 1rem;
|
||||
line-height: 24px;
|
||||
max-width: calc(100% - 1.5rem);
|
||||
margin-bottom: 1px;
|
||||
|
||||
/* @include respond-to(handhelds) {
|
||||
text-overflow: ellipsis;
|
||||
@ -109,6 +108,10 @@
|
||||
line-height: var(--line-height);
|
||||
}
|
||||
|
||||
.info:not(:empty) {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.btn-menu-toggle {
|
||||
.btn-menu {
|
||||
top: calc(100% + 7px);
|
||||
|
@ -236,6 +236,10 @@ ul.chatlist {
|
||||
padding: .0625rem .4375rem .0625rem .5625rem;
|
||||
}
|
||||
|
||||
.dialog-avatar, .user-caption {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.user-title {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
|
@ -309,3 +309,47 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-field-toggle {
|
||||
--size: 20px;
|
||||
margin: 0 .3125rem;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
|
||||
.checkbox-toggle {
|
||||
--offset: 3px;
|
||||
--toggle-width: 1.9375rem;
|
||||
width: var(--toggle-width);
|
||||
height: .875rem;
|
||||
background-color: var(--secondary-color);
|
||||
border-radius: $border-radius-big;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: background-color .2s;
|
||||
margin: 0 var(--offset);
|
||||
|
||||
&:before {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border: 2px solid var(--secondary-color);
|
||||
transition: border-color .2s, transform .2s;
|
||||
background-color: var(--surface-color);
|
||||
content: " ";
|
||||
transform: translateX(calc(var(--offset) * -1));
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + .checkbox-toggle {
|
||||
background-color: var(--primary-color);
|
||||
|
||||
&:before {
|
||||
border-color: var(--primary-color);
|
||||
transform: translateX(calc(var(--toggle-width) - 1.25rem + var(--offset)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,13 @@
|
||||
@include respond-to(esg-top) {
|
||||
position: absolute !important;
|
||||
left: $chat-padding;
|
||||
bottom: 85px;
|
||||
bottom: 84px;
|
||||
width: 420px !important;
|
||||
height: 420px;
|
||||
max-height: 420px;
|
||||
box-shadow: 0px 5px 10px 5px rgba(16, 35, 47, .14);
|
||||
z-index: 3;
|
||||
border-radius: 10px;
|
||||
border-radius: $border-radius-medium;
|
||||
transition: transform var(--esg-transition), opacity var(--esg-transition);
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
|
@ -60,17 +60,17 @@
|
||||
.menu-horizontal-scrollable {
|
||||
z-index: 1;
|
||||
background-color: var(--surface-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
position: relative;
|
||||
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .16);
|
||||
top: unset;
|
||||
height: 43px;
|
||||
|
||||
.scrollable {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-horizontal-div {
|
||||
border-bottom: none;
|
||||
height: 43px;
|
||||
position: relative !important;
|
||||
|
||||
justify-content: flex-start;
|
||||
@ -107,8 +107,8 @@
|
||||
}
|
||||
|
||||
&:not(.hide) + .scrollable {
|
||||
top: 44px;
|
||||
height: calc(100% - 44px);
|
||||
top: 43px;
|
||||
height: calc(100% - 43px);
|
||||
|
||||
#folders-container {
|
||||
margin-top: .5rem;
|
||||
@ -116,11 +116,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.folders-tabs-scrollable .menu-horizontal-div-item:first-child {
|
||||
margin-left: .6875rem;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-left: .1875rem;
|
||||
.folders-tabs-scrollable {
|
||||
.menu-horizontal-div-item:first-child {
|
||||
margin-left: .6875rem;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-left: .1875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -791,6 +793,12 @@
|
||||
|
||||
.sidebar-left {
|
||||
&-section {
|
||||
/* padding-bottom: .75rem;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding-bottom: .5rem;
|
||||
} */
|
||||
|
||||
padding: .5rem 0 1rem;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
@ -798,10 +806,8 @@
|
||||
}
|
||||
|
||||
&-content {
|
||||
margin: 0 .5rem;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin: 0 .25rem;
|
||||
@include respond-to(not-handhelds) {
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
|
||||
> .btn-primary {
|
||||
@ -813,6 +819,13 @@
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
> .checkbox-ripple,
|
||||
> .btn-primary {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-name {
|
||||
@ -841,6 +854,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// * comment later
|
||||
&:first-child:not(.no-delimiter) {
|
||||
padding-top: 0;
|
||||
}
|
||||
@ -1020,8 +1034,6 @@
|
||||
height: 66px;
|
||||
padding-top: 9px;
|
||||
padding-bottom: 9px;
|
||||
|
||||
border-radius: $border-radius-medium;
|
||||
}
|
||||
|
||||
.user-caption {
|
||||
@ -1035,7 +1047,10 @@
|
||||
|
||||
ul {
|
||||
margin-top: .3125rem;
|
||||
padding: 0 .6875rem;
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
padding: 0 .6875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,11 +282,15 @@
|
||||
}
|
||||
|
||||
&.active {
|
||||
transition: var(--open-duration) transform, var(--open-duration) border-radius;
|
||||
transition: transform var(--open-duration), border-radius var(--open-duration), opacity var(--open-duration) calc(var(--open-duration) / 8);
|
||||
}
|
||||
|
||||
&.active.opening {
|
||||
transition: transform var(--open-duration), border-radius var(--open-duration), opacity var(--open-duration) 0s;
|
||||
}
|
||||
|
||||
&.moving {
|
||||
transition: var(--move-duration) transform ease;
|
||||
transition: transform var(--move-duration) ease;
|
||||
}
|
||||
|
||||
/* &.center {
|
||||
|
@ -8,7 +8,8 @@
|
||||
&-avatars {
|
||||
&-container {
|
||||
width: 100%;
|
||||
height: 26.25rem;
|
||||
padding-bottom: 100%;
|
||||
//height: 26.25rem;
|
||||
//overflow: hidden;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
@ -23,14 +24,14 @@
|
||||
}
|
||||
|
||||
&-avatars {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
transform: translateZ(-1px) scale(2);
|
||||
transform-origin: left top;
|
||||
transition: transform .2s ease-in-out;
|
||||
position: relative;
|
||||
position: absolute;
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
@ -57,14 +58,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* &-gradient {
|
||||
&-gradient {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 80px;
|
||||
background: linear-gradient(360deg, var(--secondary-color) 8.98%, rgba(0, 0, 0, 0) 100%);
|
||||
} */
|
||||
background: linear-gradient(360deg, rgba(0, 0, 0, .3) 8.98%, rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
|
||||
&-info {
|
||||
position: absolute;
|
||||
@ -74,12 +75,17 @@
|
||||
align-items: flex-start;
|
||||
left: 1.5rem;
|
||||
bottom: .5625rem;
|
||||
pointer-events: none;
|
||||
|
||||
.profile-name, .profile-subtitle {
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.peer-typing-text-dot {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
@ -100,6 +106,7 @@
|
||||
left: .375rem;
|
||||
right: .375rem;
|
||||
height: .125rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-tab {
|
||||
@ -152,8 +159,21 @@
|
||||
position: relative;
|
||||
background-color: var(--surface-color);
|
||||
//padding-top: .5625rem;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: .5rem;
|
||||
//margin-bottom: .75rem;
|
||||
//box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .16);
|
||||
}
|
||||
|
||||
/* .search-super {
|
||||
&:before {
|
||||
content: " ";
|
||||
height: 12px;
|
||||
width: 100%;
|
||||
background-color: var(--background-color-true);
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
&-container {
|
||||
|
@ -100,6 +100,7 @@
|
||||
}
|
||||
|
||||
.shared-media-container {
|
||||
//background-color: var(--background-color-true) !important;
|
||||
/* .search-super {
|
||||
top: 100%;
|
||||
min-height: calc((var(--vh, 1vh) * 100) - 100% - 56px);
|
||||
@ -109,8 +110,12 @@
|
||||
}
|
||||
} */
|
||||
.scrollable {
|
||||
perspective: 1px;
|
||||
perspective: 0px;
|
||||
perspective-origin: left top;
|
||||
|
||||
&.parallax {
|
||||
perspective: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-super {
|
||||
@ -156,6 +161,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-super {
|
||||
.menu-horizontal-div-item {
|
||||
height: 3.5rem;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1rem;
|
||||
line-height: var(--line-height);
|
||||
}
|
||||
|
||||
.menu-horizontal-div i {
|
||||
bottom: calc(-.625rem - 7px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-super {
|
||||
@ -197,10 +219,10 @@
|
||||
flex: 1 1 auto;
|
||||
|
||||
//margin-top: 36px;
|
||||
i {
|
||||
/* i {
|
||||
padding-right: 1.5rem !important;
|
||||
margin-left: -.75rem !important;
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
&-tabs-scrollable {
|
||||
@ -211,16 +233,22 @@
|
||||
top: 0px;
|
||||
z-index: 2;
|
||||
background-color: var(--surface-color);
|
||||
height: 3.5rem;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
left: 0;
|
||||
top: -1px;
|
||||
top: 0;
|
||||
background-color: inherit;
|
||||
display: block;
|
||||
content: " ";
|
||||
z-index: -1;
|
||||
|
||||
.search-super.is-full-viewport & {
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
@ -336,16 +364,12 @@
|
||||
&-content-media &-month {
|
||||
&-items {
|
||||
width: 100%;
|
||||
padding: 7.5px;
|
||||
padding-top: 1px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3,1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
grid-gap: 3.5px;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 7.5px 7.5px 7.5px 6.5px;
|
||||
}
|
||||
grid-gap: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,4 +116,4 @@
|
||||
to {
|
||||
transform: scale(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
110
src/scss/partials/_row.scss
Normal file
110
src/scss/partials/_row.scss
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
.row {
|
||||
min-height: 3.5rem;
|
||||
position: relative;
|
||||
padding: .6875rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&-title-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
order: 0;
|
||||
|
||||
.row-title {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: var(--primary-text-color);
|
||||
line-height: var(--line-height);
|
||||
order: 0;
|
||||
|
||||
@include text-overflow(false);
|
||||
|
||||
&-right {
|
||||
flex: 0 0 auto !important;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-midtitle {
|
||||
font-size: .875rem;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
&-with-padding {
|
||||
padding-left: 4.5rem;
|
||||
|
||||
.row-title.tgico:before {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
font-size: 1.5rem;
|
||||
color: var(--secondary-text-color);
|
||||
pointer-events: none;
|
||||
margin-top: -.125rem;
|
||||
}
|
||||
|
||||
.row-subtitle:not(:empty) + .row-title.tgico:before {
|
||||
margin-top: .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-clickable {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
border-radius: $border-radius-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-field-main, .checkbox-field {
|
||||
padding-left: 3.375rem;
|
||||
margin-left: -3.375rem;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
margin-right: 0;
|
||||
height: auto;
|
||||
|
||||
.checkbox-caption {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-field-toggle {
|
||||
margin: 0;
|
||||
margin-right: .125rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-subtitle {
|
||||
color: var(--secondary-text-color) !important;
|
||||
font-size: .875rem !important;
|
||||
line-height: var(--line-height);
|
||||
margin-top: .125rem;
|
||||
margin-bottom: .0625rem;
|
||||
order: 1;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,23 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
.menu-horizontal-scrollable {
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.menu-horizontal-div {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-horizontal-div {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@ -64,7 +81,7 @@
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
bottom: calc(-.625rem - 2px);
|
||||
bottom: calc(-.625rem - 3px);
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
background-color: var(--primary-color);
|
||||
@ -72,8 +89,8 @@
|
||||
width: 100%;
|
||||
border-radius: .1875rem .1875rem 0 0;
|
||||
pointer-events: none;
|
||||
padding-right: .5rem;
|
||||
margin-left: -.25rem;
|
||||
/* padding-right: .5rem;
|
||||
margin-left: -.25rem; */
|
||||
box-sizing: content-box;
|
||||
transform-origin: left;
|
||||
z-index: 1;
|
||||
|
@ -43,8 +43,7 @@
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
&:before, &:after {
|
||||
content: " ";
|
||||
.auth-placeholder {
|
||||
flex: 1;
|
||||
min-height: 3rem;
|
||||
/* height: 105px; */
|
||||
@ -52,7 +51,7 @@
|
||||
}
|
||||
|
||||
@media screen and (max-height: 810px) {
|
||||
&:after {
|
||||
.auth-placeholder:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -64,13 +64,14 @@
|
||||
position: relative;
|
||||
|
||||
.btn-primary {
|
||||
width: 79px;
|
||||
width: auto;
|
||||
height: 36px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
padding: 0;
|
||||
padding: 0 1.375rem;
|
||||
margin-top: -3px;
|
||||
border-radius: $border-radius-medium;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +82,7 @@
|
||||
|
||||
&-title {
|
||||
flex: 1;
|
||||
padding: 0 2rem 0 1.5rem;
|
||||
padding-left: 1.5rem;
|
||||
margin: 0;
|
||||
margin-top: -3px;
|
||||
font-size: 1.25rem;
|
||||
|
@ -118,7 +118,7 @@ $chat-padding-handhelds: .5rem;
|
||||
:root {
|
||||
// * Day theme
|
||||
--body-background-color: #fff;
|
||||
//--background-color: #f4f4f5;
|
||||
--background-color-true: #f4f4f5;
|
||||
--background-color: #fff;
|
||||
--border-color: #dfe1e5;
|
||||
--surface-color: #fff;
|
||||
@ -136,6 +136,8 @@ $chat-padding-handhelds: .5rem;
|
||||
@include splitColor(danger-color, #df3f40, true, false);
|
||||
|
||||
--avatar-online-color: #0ac630;
|
||||
--avatar-color-top: var(--peer-avatar-blue-top);
|
||||
--avatar-color-bottom: var(--peer-avatar-blue-bottom);
|
||||
--chatlist-status-color: var(--avatar-online-color);
|
||||
--chatlist-pinned-color: #a2abb2;
|
||||
--badge-text-color: #fff;
|
||||
@ -159,7 +161,7 @@ html.night {
|
||||
//:root {
|
||||
// * Night theme
|
||||
--body-background-color: #181818;
|
||||
//--background-color: #181818;
|
||||
--background-color-true: #181818;
|
||||
--background-color: #212121;
|
||||
--border-color: #0f0f0f;
|
||||
--surface-color: #212121;
|
||||
@ -177,6 +179,8 @@ html.night {
|
||||
@include splitColor(danger-color, #ff595a, true, false);
|
||||
|
||||
--avatar-online-color: #0ac630;
|
||||
--avatar-color-top: var(--peer-avatar-violet-top);
|
||||
--avatar-color-bottom: var(--peer-avatar-violet-bottom);
|
||||
--chatlist-status-color: var(--primary-color);
|
||||
--chatlist-pinned-color: var(--secondary-color);
|
||||
--badge-text-color: #fff;
|
||||
@ -231,6 +235,7 @@ html.night {
|
||||
@import "partials/peerTyping";
|
||||
@import "partials/poll";
|
||||
@import "partials/transition";
|
||||
@import "partials/row";
|
||||
|
||||
@import "partials/popups/popup";
|
||||
@import "partials/popups/editAvatar";
|
||||
@ -1009,94 +1014,6 @@ middle-ellipsis-element {
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
min-height: 3.5rem;
|
||||
position: relative;
|
||||
padding: .6875rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&-title-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.row-title {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: var(--primary-text-color);
|
||||
line-height: var(--line-height);
|
||||
|
||||
@include text-overflow(false);
|
||||
|
||||
&-right {
|
||||
flex: 0 0 auto !important;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-midtitle {
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
&-with-padding {
|
||||
padding-left: 4.5rem;
|
||||
|
||||
.row-title.tgico:before {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
font-size: 1.5rem;
|
||||
margin-top: .25rem;
|
||||
color: var(--secondary-text-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-clickable {
|
||||
cursor: pointer;
|
||||
border-radius: $border-radius-medium;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.radio-field-main, .checkbox-field {
|
||||
padding-left: 3.375rem;
|
||||
margin-left: -3.375rem;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
margin-right: 0;
|
||||
height: auto;
|
||||
|
||||
.checkbox-caption {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-subtitle {
|
||||
color: var(--secondary-text-color) !important;
|
||||
font-size: .875rem !important;
|
||||
line-height: var(--line-height);
|
||||
margin-top: .125rem;
|
||||
margin-bottom: .0625rem;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hover-effect {
|
||||
@include hover-background-effect();
|
||||
}
|
||||
@ -1193,3 +1110,21 @@ middle-ellipsis-element {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gradient-delimiter {
|
||||
width: 100%;
|
||||
height: .75rem;
|
||||
display: flex;
|
||||
background-color: var(--background-color-true);
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, .06) 0%, rgba(0, 0, 0, 0) 20%, rgba(0, 0, 0, 0) 94%, rgba(0, 0, 0, .06) 100%);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user