diff --git a/public/assets/img/pattern_newbroken.svg b/public/assets/img/pattern_newbroken.svg new file mode 100644 index 000000000..05984b97a --- /dev/null +++ b/public/assets/img/pattern_newbroken.svg @@ -0,0 +1,4649 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/appSearch.ts b/src/components/appSearch.ts index 149608bb5..88be9721e 100644 --- a/src/components/appSearch.ts +++ b/src/components/appSearch.ts @@ -204,7 +204,7 @@ export default class AppSearch { appDialogsManager.addDialogAndSetLastMessage({ peerId, container: this.scrollable/* searchGroup.list */, - avatarSize: 54, + avatarSize: 'bigger', meAsSaved: false, message, query, diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index 2a86ffa53..54b7474f5 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -73,6 +73,7 @@ import SwipeHandler from './swipeHandler'; import wrapDocument from './wrappers/document'; import wrapPhoto from './wrappers/photo'; import wrapVideo from './wrappers/video'; +import noop from '../helpers/noop'; // const testScroll = false; @@ -610,7 +611,7 @@ export default class AppSearchSuper { const {dom} = appDialogsManager.addDialogNew({ peerId: message.peerId, container: searchGroup.list, - avatarSize: 54, + avatarSize: 'bigger', loadPromises }); @@ -625,7 +626,7 @@ export default class AppSearchSuper { }); loadPromises.push(setLastMessagePromise); - return Promise.all(loadPromises); + return Promise.all(loadPromises).then(noop); } private async processPhotoVideoFilter({message, promises, middleware}: ProcessSearchSuperResult) { @@ -705,10 +706,8 @@ export default class AppSearchSuper { let url: string, display_url: string, sliced: string; if(!entity) { - // this.log.error('NO ENTITY:', message); const match = matchUrl(message.message); if(!match) { - // this.log.error('NO ENTITY AND NO MATCH:', message); return; } @@ -748,7 +747,7 @@ export default class AppSearchSuper { } const previewDiv = document.createElement('div'); - previewDiv.classList.add('preview', 'row-media', 'row-media-big'); + previewDiv.classList.add('preview'); // this.log('wrapping webpage', webpage); @@ -808,19 +807,7 @@ export default class AppSearchSuper { noRipple: true }); - /* const mediaDiv = document.createElement('div'); - mediaDiv.classList.add('row-media'); */ - - row.container.append(previewDiv); - - /* ripple(div); - div.append(previewDiv); - div.insertAdjacentHTML('beforeend', ` -
${title}${titleAdditionHTML}
-
${subtitle}
-
${url}
- ${sender} - `); */ + row.applyMediaElement(previewDiv, 'big'); if(row.container.innerText.trim().length) { return {message, element: row.container}; @@ -923,6 +910,10 @@ export default class AppSearchSuper { const method = append ? 'append' : 'prepend'; elemsToAppend.forEach((details) => { const {element, message} = details; + if(!message) { + debugger; + } + const monthContainer = this.getMonthContainerByTimestamp(this.groupByMonth ? message.date : 0, inputFilter); element.classList.add('search-super-item'); element.dataset.mid = '' + message.mid; @@ -982,12 +973,12 @@ export default class AppSearchSuper { const {dom} = appDialogsManager.addDialogNew({ peerId: peerId, container: group.list, - avatarSize: 48, + avatarSize: 'abitbigger', autonomous: group.autonomous }); return {dom, peerId}; - }).forEach(async({dom, peerId}) => { + }).filter(Boolean).forEach(async({dom, peerId}) => { const peer = await this.managers.appPeersManager.getPeer(peerId); if(showMembersCount && (peer.participants_count || peer.participants)) { const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(cleanSearchText(query))})`, 'gi'); @@ -1006,7 +997,7 @@ export default class AppSearchSuper { username = '@' + username; } - dom.lastMessageSpan.innerHTML = '' + username + ''; + dom.lastMessageSpan.textContent = username; } }); @@ -1024,7 +1015,7 @@ export default class AppSearchSuper { }; return Promise.all([ - this.managers.appUsersManager.getContactsPeerIds(query, true) + this.managers.appUsersManager.getContactsPeerIds(query, true, undefined, 10) .then(onLoad) .then((contacts) => { if(contacts) { @@ -1084,7 +1075,7 @@ export default class AppSearchSuper { peerId: peerId, container: this.searchGroups.recent.list, meAsSaved: true, - avatarSize: 48, + avatarSize: 'abitbigger', autonomous: true }); @@ -1113,14 +1104,16 @@ export default class AppSearchSuper { // console.log('got top categories:', categories); if(peers.length) { peers.forEach((peer) => { - appDialogsManager.addDialogNew({ + const {dom} = appDialogsManager.addDialogNew({ peerId: peer.id, container: this.searchGroups.people.list, onlyFirstName: true, - avatarSize: 54, + avatarSize: 'bigger', autonomous: false, noIcons: this.searchGroups.people.noIcons }); + + dom.subtitleEl.remove(); }); } diff --git a/src/components/appSelectPeers.ts b/src/components/appSelectPeers.ts index c43fe9cbf..386db8102 100644 --- a/src/components/appSelectPeers.ts +++ b/src/components/appSelectPeers.ts @@ -6,7 +6,7 @@ import type {ChatRights} from '../lib/appManagers/appChatsManager'; import type {Dialog} from '../lib/appManagers/appMessagesManager'; -import appDialogsManager from '../lib/appManagers/appDialogsManager'; +import appDialogsManager, {DialogElementSize as DialogElementSize} from '../lib/appManagers/appDialogsManager'; import rootScope from '../lib/rootScope'; import Scrollable from './scrollable'; import {FocusDirection} from '../helpers/fastSmoothScroll'; @@ -77,7 +77,7 @@ export default class AppSelectPeers { private chatRightsAction: ChatRights; private multiSelect = true; private rippleEnabled = true; - private avatarSize = 48; + private avatarSize: DialogElementSize = 'abitbigger'; private exceptSelf = false; private filterPeerTypeBy: IsPeerType[]; @@ -94,6 +94,8 @@ export default class AppSelectPeers { private managers: AppManagers; + private design: 'round' | 'square' = 'round'; + constructor(options: { appendTo: AppSelectPeers['appendTo'], onChange?: AppSelectPeers['onChange'], @@ -110,11 +112,12 @@ export default class AppSelectPeers { exceptSelf?: AppSelectPeers['exceptSelf'], filterPeerTypeBy?: AppSelectPeers['filterPeerTypeBy'], sectionNameLangPackKey?: AppSelectPeers['sectionNameLangPackKey'], - managers: AppSelectPeers['managers'] + managers: AppSelectPeers['managers'], + design?: AppSelectPeers['design'] }) { safeAssign(this, options); - this.container.classList.add('selector'); + this.container.classList.add('selector', 'selector-' + this.design); const f = (this.renderResultsFunc || this.renderResults).bind(this); this.renderResultsFunc = async(peerIds) => { @@ -313,7 +316,7 @@ export default class AppSelectPeers { } // в десктопе - сначала без группы, потом архивные, потом контакты без сообщений - const pageCount = windowSize.height / 72 * 1.25 | 0; + const pageCount = windowSize.height / 56 * 1.25 | 0; const tempId = this.getTempId('dialogs'); const promise = this.managers.appMessagesManager.getConversations(this.query, this.offsetIndex, pageCount, this.folderId, true); @@ -420,7 +423,7 @@ export default class AppSelectPeers { } // if(this.cachedContacts.length) { - const pageCount = windowSize.height / 72 * 1.25 | 0; + const pageCount = windowSize.height / 56 * 1.25 | 0; const arr = this.cachedContacts.splice(0, pageCount); this.renderResultsFunc(arr); // } diff --git a/src/components/chat/bubbleGroups.ts b/src/components/chat/bubbleGroups.ts index 09c3ac96e..8de8c206d 100644 --- a/src/components/chat/bubbleGroups.ts +++ b/src/components/chat/bubbleGroups.ts @@ -289,7 +289,11 @@ export default class BubbleGroups { removeAndUnmountBubble(bubble: HTMLElement) { const item = this.getItemByBubble(bubble); if(!item) { - return; + if(bubble.parentElement) { + bubble.remove(); // * can be a placeholder + } + + return false; } const items = this.itemsArr; @@ -320,6 +324,8 @@ export default class BubbleGroups { } this.mountUnmountGroups(Array.from(modifiedGroups)); + + return true; } mountUnmountGroups(groups: BubbleGroup[]) { diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index af20197b3..67ac46736 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -2046,23 +2046,15 @@ export default class ChatBubbles { // return; if(this.isHeavyAnimationInProgress) { - if(this.sliceViewportDebounced) { - this.sliceViewportDebounced.clearTimeout(); - } + this.sliceViewportDebounced?.clearTimeout(); // * В таком случае, кнопка не будет моргать если чат в самом низу, и правильно отработает случай написания нового сообщения и проскролла вниз if(this.scrolledDown && !ignoreHeavyAnimation) { return; } } else { - if(this.chat.topbar.pinnedMessage) { - this.chat.topbar.pinnedMessage.setCorrectIndexThrottled(this.scrollable.lastScrollDirection); - } - - if(this.sliceViewportDebounced) { - this.sliceViewportDebounced(); - } - + this.chat.topbar.pinnedMessage?.setCorrectIndexThrottled(this.scrollable.lastScrollDirection); + this.sliceViewportDebounced?.(); this.setStickyDateManually(); } @@ -2172,41 +2164,46 @@ export default class ChatBubbles { } } + public destroyBubble(bubble: HTMLElement, mid = +bubble.dataset.mid) { + // this.log.warn('destroy bubble', bubble, mid); + bubble.middlewareHelper.destroy(); + + /* const mounted = this.getMountedBubble(mid); + if(!mounted) return; */ + + if(this.bubbles[mid] === bubble) { // have to check because can clear bubble with same id later + delete this.bubbles[mid]; + } + + this.skippedMids.delete(mid); + + if(this.firstUnreadBubble === bubble) { + this.firstUnreadBubble = null; + } + + this.bubbleGroups.removeAndUnmountBubble(bubble); + if(this.observer) { + this.observer.unobserve(bubble, this.unreadedObserverCallback); + this.unreaded.delete(bubble); + + this.observer.unobserve(bubble, this.viewsObserverCallback); + this.viewsMids.delete(mid); + + this.observer.unobserve(bubble, this.stickerEffectObserverCallback); + } + + // this.reactions.delete(mid); + } + public deleteMessagesByIds(mids: number[], permanent = true, ignoreOnScroll?: boolean) { let deleted = false; mids.forEach((mid) => { const bubble = this.bubbles[mid]; if(!bubble) return; - bubble.middlewareHelper.destroy(); + this.destroyBubble(bubble, mid); deleted = true; - /* const mounted = this.getMountedBubble(mid); - if(!mounted) return; */ - - delete this.bubbles[mid]; - this.skippedMids.delete(mid); - - if(this.firstUnreadBubble === bubble) { - this.firstUnreadBubble = null; - } - - this.bubbleGroups.removeAndUnmountBubble(bubble); - if(this.observer) { - this.observer.unobserve(bubble, this.unreadedObserverCallback); - this.unreaded.delete(bubble); - - this.observer.unobserve(bubble, this.viewsObserverCallback); - this.viewsMids.delete(mid); - - this.observer.unobserve(bubble, this.stickerEffectObserverCallback); - } - - if(this.emptyPlaceholderBubble === bubble) { - this.emptyPlaceholderBubble = undefined; - } - - // this.reactions.delete(mid); }); if(!deleted) { @@ -2552,53 +2549,54 @@ export default class ChatBubbles { public getDateContainerByTimestamp(timestamp: number) { const {date, dateTimestamp} = this.getDateForDateContainer(timestamp); - if(!this.dateMessages[dateTimestamp]) { - const bubble = this.createDateBubble(timestamp, date); - // bubble.classList.add('is-sticky'); - const fakeBubble = this.createDateBubble(timestamp, date); - fakeBubble.classList.add('is-fake'); + let ret = this.dateMessages[dateTimestamp]; + if(ret) { + return ret; + } - const container = document.createElement('section'); - container.className = 'bubbles-date-group'; - container.append(bubble, fakeBubble); + const bubble = this.createDateBubble(timestamp, date); + // bubble.classList.add('is-sticky'); + const fakeBubble = this.createDateBubble(timestamp, date); + fakeBubble.classList.add('is-fake'); - this.dateMessages[dateTimestamp] = { - div: bubble, - container, - firstTimestamp: date.getTime() - }; + const container = document.createElement('section'); + container.className = 'bubbles-date-group'; + container.append(bubble, fakeBubble); - const haveTimestamps = getObjectKeysAndSort(this.dateMessages, 'asc'); - const length = haveTimestamps.length; - let i = 0, insertBefore: HTMLElement; // there can be 'first bubble' (e.g. bot description) so can't insert by index - for(; i < haveTimestamps.length; ++i) { - const t = haveTimestamps[i]; - insertBefore = this.dateMessages[t].container; - if(dateTimestamp < t) { - break; - } - } + ret = this.dateMessages[dateTimestamp] = { + div: bubble, + container, + firstTimestamp: date.getTime() + }; - if(i === length && insertBefore) { - insertBefore = insertBefore.nextElementSibling as HTMLElement; - } - - if(!insertBefore) { - this.chatInner.append(container); - } else { - this.chatInner.insertBefore(container, insertBefore); - } - - if(this.stickyIntersector) { - this.stickyIntersector.observeStickyHeaderChanges(container); - } - - if(this.chatInner.parentElement) { - this.container.classList.add('has-groups'); + const haveTimestamps = getObjectKeysAndSort(this.dateMessages, 'asc'); + const length = haveTimestamps.length; + let i = 0, insertBefore: HTMLElement; // there can be 'first bubble' (e.g. bot description) so can't insert by index + for(; i < haveTimestamps.length; ++i) { + const t = haveTimestamps[i]; + insertBefore = this.dateMessages[t].container; + if(dateTimestamp < t) { + break; } } - return this.dateMessages[dateTimestamp]; + if(i === length && insertBefore) { + insertBefore = insertBefore.nextElementSibling as HTMLElement; + } + + if(!insertBefore) { + this.chatInner.append(container); + } else { + this.chatInner.insertBefore(container, insertBefore); + } + + this.stickyIntersector?.observeStickyHeaderChanges(container); + + if(this.chatInner.parentElement) { + this.container.classList.add('has-groups'); + } + + return ret; } private destroyScrollable() { @@ -2622,6 +2620,8 @@ export default class ChatBubbles { } public cleanup(bubblesToo = false) { + this.log('cleanup'); + this.bubbles = {}; // clean it before so sponsored message won't be deleted faster on peer changing // //console.time('appImManager cleanup'); this.setLoaded('top', false, false); @@ -2707,11 +2707,7 @@ export default class ChatBubbles { private cleanupPlaceholders(bubble = this.emptyPlaceholderBubble) { if(bubble) { - bubble.remove(); - - if(this.emptyPlaceholderBubble === bubble) { - this.emptyPlaceholderBubble = undefined; - } + this.destroyBubble(bubble); } } @@ -4748,9 +4744,12 @@ export default class ChatBubbles { }; } - public async performHistoryResult(historyResult: HistoryResult | {history: (Message.message | Message.messageService | number)[]}, reverse: boolean) { + public async performHistoryResult( + historyResult: HistoryResult | {history: (Message.message | Message.messageService | number)[]}, + reverse: boolean + ) { const log = false ? this.log.bindPrefix('perform-' + (Math.random() * 1000 | 0)) : undefined; - log && log('start', this.chatInner.parentElement); + log?.('start', this.chatInner.parentElement); let history = historyResult.history; history = history.slice(); // need @@ -4817,13 +4816,10 @@ export default class ChatBubbles { if(this.scrollable.loadedAll.top && this.messagesQueueOnRenderAdditional) { this.messagesQueueOnRenderAdditional(); - - if(this.messagesQueueOnRenderAdditional) { - this.messagesQueueOnRenderAdditional(); - } + this.messagesQueueOnRenderAdditional?.(); // * can set it second time } - log && log('performHistoryResult end'); + log?.('performHistoryResult end'); } private onRenderScrollSet(state?: {scrollHeight: number, clientHeight: number}) { @@ -5174,16 +5170,22 @@ export default class ChatBubbles { elements.forEach((element: any) => element.classList.add(BASE_CLASS + '-line')); } - private async processLocalMessageRender(message: Message.message | Message.messageService, animate?: boolean) { + private async processLocalMessageRender( + message: Message.message | Message.messageService, + animate?: boolean, + middleware = this.getMiddleware() + ) { const isSponsored = !!(message as Message.message).pFlags.sponsored; - const middleware = this.getMiddleware(); const m = middlewarePromise(middleware); - return this.safeRenderMessage(message, isSponsored ? false : true, undefined, isSponsored, async(result) => { + + const p: Parameters[4] = async(result) => { const {bubble} = await m(result); if(!bubble) { return result; } + (bubble as any).message = message; + bubble.classList.add('is-group-last', 'is-group-first'); const updatePosition = () => { @@ -5208,6 +5210,7 @@ export default class ChatBubbles { let text: LangPackKey, mid: number, startParam: string, callback: () => void; bubble.classList.add('avoid-selection'); + bubble.style.order = '999999'; const sponsoredMessage = this.sponsoredMessage = (message as Message.message).sponsoredMessage; const peerId = getPeerId(sponsoredMessage.from_id); @@ -5257,14 +5260,18 @@ export default class ChatBubbles { bubble.querySelector('.bubble-content').prepend(button); - return result; + appendTo = this.chatInner; + method = 'append'; + animate = false; + + // return result; } else if(isBot && message._ === 'message') { const b = document.createElement('b'); b.append(i18n('BotInfoTitle')); elements.push(b, '\n\n'); appendTo = this.chatInner; method = 'prepend'; - } else if(await m(this.managers.appPeersManager.isAnyGroup(this.peerId)) && (await m(this.managers.appPeersManager.getPeer(this.peerId))).pFlags.creator) { + } else if(this.chat.isAnyGroup && (await m(this.managers.appPeersManager.getPeer(this.peerId))).pFlags.creator) { renderPromise = this.renderEmptyPlaceholder('group', bubble, message, elements); } else if(this.chat.type === 'scheduled') { renderPromise = this.renderEmptyPlaceholder('noScheduledMessages', bubble, message, elements); @@ -5332,12 +5339,24 @@ export default class ChatBubbles { this.animateAsLadder(message.mid, additionMsgIds, false, 0, 0); } - // if(!isSponsored) { + bubble.middlewareHelper.onDestroy(() => { + if(this.emptyPlaceholderBubble === bubble) { + this.emptyPlaceholderBubble = undefined; + } + }); + this.emptyPlaceholderBubble = bubble; - // } return result; - }); + }; + + return this.safeRenderMessage( + message, + !isSponsored, + undefined, + false, + p + ); } private generateLocalMessageId(addOffset = 0) { @@ -5471,19 +5490,23 @@ export default class ChatBubbles { } private async toggleSponsoredMessage(value: boolean) { - const _log = this.log.bindPrefix('sponsored'); - _log('checking'); + const log = this.log.bindPrefix('sponsored'); + log('checking'); const {mid} = this.generateLocalMessageId(SPONSORED_MESSAGE_ID_OFFSET); if(value) { const middleware = this.getMiddleware(() => { - return this.scrollable.loadedAll.bottom && !this.bubbles[mid] && this.getSponsoredMessagePromise === promise; + return this.scrollable.loadedAll.bottom && this.getSponsoredMessagePromise === promise; }); const promise = this.getSponsoredMessagePromise = this.managers.appChatsManager.getSponsoredMessage(this.peerId.toChatId()) .then(async(sponsoredMessages) => { + if(!middleware()) { + return; + } + const sponsoredMessage = sponsoredMessages.messages[0]; if(!sponsoredMessage) { - _log('no message'); + log('no message'); return; } @@ -5502,16 +5525,18 @@ export default class ChatBubbles { ]).then(([message]) => { if(!middleware()) return; // this.processLocalMessageRender(message); - _log('rendering', message); - const promise = this.performHistoryResult({history: [message]}, false); + log('rendering', message); + return this.performHistoryResult({history: [message]}, false); }); }).finally(() => { - this.getSponsoredMessagePromise = undefined; + if(this.getSponsoredMessagePromise === promise) { + this.getSponsoredMessagePromise = undefined; + } }); } else { - _log('clearing rendered', mid); - this.deleteMessagesByIds([mid]); + log('clearing rendered', mid); this.getSponsoredMessagePromise = undefined; + this.deleteMessagesByIds([mid]); } } @@ -5565,7 +5590,7 @@ export default class ChatBubbles { this.chat.isRestricted || !(await this.chat.getHistoryStorage()).count || ( - !Object.keys(this.bubbles).length || + // !Object.keys(this.bubbles).length || // ! WARNING ! ! ! ! ! ! REPLACE LINE ABOVE WITH THESE Object.keys(this.bubbles).length && !this.getRenderedLength() diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 05eb2f890..3c06df60e 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -398,6 +398,7 @@ export default class Chat extends EventListenerBase<{ public destroy() { // const perf = performance.now(); + this.destroySharedMediaTab(); this.topbar.destroy(); this.bubbles.destroy(); this.input.destroy(); @@ -523,6 +524,10 @@ export default class Chat extends EventListenerBase<{ } public destroySharedMediaTab(tab = this.sharedMediaTab) { + if(!tab) { + return; + } + indexOfAndSplice(this.sharedMediaTabs, tab); tab.destroy(); } diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index 7618e8f14..028606dc3 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -184,7 +184,7 @@ export default class ChatContextMenu { } this.isSelected = this.chat.selection.isMidSelected(this.peerId, this.mid); - this.message = await this.chat.getMessage(this.mid); + this.message = (bubble as any).message || await this.chat.getMessage(this.mid); this.noForwards = !isSponsored && !(await this.managers.appMessagesManager.canForward(this.message)); this.viewerPeerId = undefined; this.canOpenReactedList = undefined; @@ -703,7 +703,13 @@ export default class ChatContextMenu { let menuPadding: MenuPositionPadding; let reactionsMenu: ChatReactionsMenu; let reactionsMenuPosition: 'horizontal' | 'vertical'; - if(this.message._ === 'message' && !this.chat.selection.isSelecting && !this.message.pFlags.is_outgoing && !this.message.pFlags.is_scheduled) { + if( + this.message._ === 'message' && + !this.chat.selection.isSelecting && + !this.message.pFlags.is_outgoing && + !this.message.pFlags.is_scheduled && + !this.message.pFlags.local + ) { reactionsMenuPosition = (IS_APPLE || IS_TOUCH_SUPPORTED)/* && false */ ? 'horizontal' : 'vertical'; reactionsMenu = this.reactionsMenu = new ChatReactionsMenu(this.managers, reactionsMenuPosition, this.middleware); reactionsMenu.init(await this.managers.appMessagesManager.getGroupsFirstMessage(this.message)); @@ -876,11 +882,11 @@ export default class ChatContextMenu { }; private onRetractVote = () => { - this.managers.appPollsManager.sendVote(this.message, []); + this.managers.appPollsManager.sendVote(this.message as Message.message, []); }; private onStopPoll = () => { - this.managers.appPollsManager.stopPoll(this.message); + this.managers.appPollsManager.stopPoll(this.message as Message.message); }; private onForwardClick = async() => { diff --git a/src/components/groupCall/participantsList.ts b/src/components/groupCall/participantsList.ts index 5118be7f7..3234df7f3 100644 --- a/src/components/groupCall/participantsList.ts +++ b/src/components/groupCall/participantsList.ts @@ -8,8 +8,7 @@ import positionElementByIndex from '../../helpers/dom/positionElementByIndex'; import replaceContent from '../../helpers/dom/replaceContent'; import {fastRaf} from '../../helpers/schedulers'; import SortedList, {SortedElementBase} from '../../helpers/sortedList'; -import {GroupCallParticipant} from '../../layer'; -import appDialogsManager, {DialogDom, AppDialogsManager} from '../../lib/appManagers/appDialogsManager'; +import appDialogsManager, {DialogDom, AppDialogsManager, DialogElementSize} from '../../lib/appManagers/appDialogsManager'; import {getGroupCallParticipantMutedState} from '.'; import GroupCallParticipantMutedIcon from './participantMutedIcon'; import GroupCallParticipantStatusElement from './participantStatus'; @@ -26,7 +25,7 @@ export default class GroupCallParticipantsList extends SortedList[0] = {/* new: true, */dialogSize: 72}; diff --git a/src/components/peerProfile.ts b/src/components/peerProfile.ts index c7ae55ca8..fdc4adb68 100644 --- a/src/components/peerProfile.ts +++ b/src/components/peerProfile.ts @@ -442,9 +442,7 @@ export default class PeerProfile { public setPeer(peerId: PeerId, threadId = 0) { if(this.peerId === peerId && this.threadId === threadId) return; - if(this.init) { - this.init(); - } + this.init?.(); this.peerId = peerId; this.threadId = threadId; @@ -460,6 +458,7 @@ export default class PeerProfile { } public destroy() { + this.peerId = this.threadId = undefined; this.clearSetMoreDetailsTimeout(); clearInterval(this.setPeerStatusInterval); this.avatars?.cleanup(); diff --git a/src/components/popups/forward.ts b/src/components/popups/forward.ts index 14970d658..a412b1211 100644 --- a/src/components/popups/forward.ts +++ b/src/components/popups/forward.ts @@ -30,5 +30,8 @@ export default class PopupForward extends PopupPickUser { chatRightsAction: 'send_messages', selfPresence: 'ChatYourSelf' }); + + this.scrollable = this.selector.scrollable; + this.attachScrollableListeners(this.scrollable); } } diff --git a/src/components/popups/index.ts b/src/components/popups/index.ts index 1347c19f0..5e31572e4 100644 --- a/src/components/popups/index.ts +++ b/src/components/popups/index.ts @@ -159,12 +159,7 @@ export default class PopupElement extends if(options.scrollable) { const scrollable = this.scrollable = new Scrollable(this.body); - scrollable.onAdditionalScroll = () => { - scrollable.container.classList.toggle('scrolled-top', !scrollable.scrollTop); - scrollable.container.classList.toggle('scrolled-bottom', scrollable.isScrolledDown); - }; - - scrollable.container.classList.add('scrolled-top', 'scrolled-bottom', 'scrollable-y-bordered'); + this.attachScrollableListeners(scrollable); if(!this.body) { this.container.insertBefore(scrollable.container, this.header.nextSibling); @@ -215,6 +210,17 @@ export default class PopupElement extends PopupElement.POPUPS.push(this); } + protected attachScrollableListeners(scrollable: Scrollable) { + const cb = scrollable.onAdditionalScroll; + scrollable.onAdditionalScroll = () => { + cb?.(); + scrollable.container.classList.toggle('scrolled-top', !scrollable.scrollTop); + scrollable.container.classList.toggle('scrolled-bottom', scrollable.isScrolledDown); + }; + + scrollable.container.classList.add('scrolled-top', 'scrolled-bottom', 'scrollable-y-bordered'); + } + protected onContentUpdate() { if(this.scrollable) { this.scrollable.onAdditionalScroll(); @@ -290,6 +296,7 @@ export default class PopupElement extends this.element.remove(); this.dispatchEvent('closeAfterTimeout'); this.cleanup(); + this.scrollable?.destroy(); if(!this.withoutOverlay) { animationIntersector.checkAnimations2(false); diff --git a/src/components/popups/pickUser.ts b/src/components/popups/pickUser.ts index 51d507486..a620fff22 100644 --- a/src/components/popups/pickUser.ts +++ b/src/components/popups/pickUser.ts @@ -54,7 +54,7 @@ export default class PopupPickUser extends PopupElement { chatRightsAction: options.chatRightsAction, multiSelect: false, rippleEnabled: false, - avatarSize: 46, + avatarSize: 'abitbigger', peerId: options.peerId, placeholder: options.placeholder, selfPresence: options.selfPresence, diff --git a/src/components/popups/reactedList.ts b/src/components/popups/reactedList.ts index 1659547ce..8957a4cfa 100644 --- a/src/components/popups/reactedList.ts +++ b/src/components/popups/reactedList.ts @@ -144,7 +144,7 @@ export default class PopupReactedList extends PopupElement { peerId: peerId, autonomous: true, container: chatlist, - avatarSize: 54, + avatarSize: 'abitbigger', rippleEnabled: false, meAsSaved: false }); diff --git a/src/components/row.ts b/src/components/row.ts index 2b376fcaa..6034f4ffc 100644 --- a/src/components/row.ts +++ b/src/components/row.ts @@ -16,12 +16,30 @@ import {attachClickEvent} from '../helpers/dom/clickEvent'; import ListenerSetter from '../helpers/listenerSetter'; import Button from './button'; +type K = string | HTMLElement | DocumentFragment | true; + +const setContent = (element: HTMLElement, content: K) => { + if(content === true) { + + } else if(typeof(content) === 'string') { + setInnerHTML(element, content); + } else { + element.append(content); + } +}; + +export type RowMediaSizeType = 'small' | 'medium' | 'big' | 'abitbigger' | 'bigger'; + export default class Row { public container: HTMLElement; - public title: HTMLDivElement; + public title: HTMLElement; + public titleRow: HTMLElement; public titleRight: HTMLElement; public media: HTMLElement; + public subtitleRow: HTMLElement; + public subtitleRight: HTMLElement; + public checkboxField: CheckboxField; public radioField: RadioField; @@ -33,17 +51,18 @@ export default class Row { constructor(options: Partial<{ icon: string, - subtitle: string | HTMLElement | DocumentFragment, + subtitle: K, subtitleLangKey: LangPackKey, subtitleLangArgs: any[], + subtitleRight: K, radioField: Row['radioField'], checkboxField: Row['checkboxField'], checkboxFieldOptions: CheckboxFieldOptions, withCheckboxSubtitle: boolean, - title: string | HTMLElement | DocumentFragment, + title: K, titleLangKey: LangPackKey, - titleRight: string | HTMLElement, - titleRightSecondary: string | HTMLElement, + titleRight: K, + titleRightSecondary: K, clickable: boolean | ((e: Event) => void), navigationTab: SliderSuperTab, havePadding: boolean, @@ -51,7 +70,8 @@ export default class Row { noWrap: boolean, listenerSetter: ListenerSetter, buttonRight?: HTMLElement | boolean, - buttonRightLangKey: LangPackKey + buttonRightLangKey: LangPackKey, + asLink: boolean }> = {}) { if(options.checkboxFieldOptions) { options.checkboxField = new CheckboxField({ @@ -60,14 +80,28 @@ export default class Row { }); } - this.container = document.createElement(options.radioField || options.checkboxField ? 'label' : 'div'); + const tagName = options.asLink ? 'a' : (options.radioField || options.checkboxField ? 'label' : 'div'); + this.container = document.createElement(tagName); this.container.classList.add('row', 'no-subtitle'); + if(options.noWrap) { + this.container.classList.add('no-wrap'); + } + if(options.subtitle) { - if(typeof(options.subtitle) === 'string') { - setInnerHTML(this.subtitle, options.subtitle); - } else { - this.subtitle.append(options.subtitle); + const subtitle = this.subtitle; + setContent(subtitle, options.subtitle); + + if(options.noWrap) subtitle.classList.add('no-wrap'); + + if(options.subtitleRight) { + this.container.append(this.subtitleRow = this.createRow()); + this.subtitleRow.classList.add('row-subtitle-row'); + const subtitleRight = this.subtitleRight = document.createElement('div'); + subtitleRight.classList.add('row-subtitle', 'row-subtitle-right'); + + setContent(subtitleRight, options.subtitleRight); + this.subtitleRow.append(subtitle, subtitleRight); } } else if(options.subtitleLangKey) { this.subtitle.append(i18n(options.subtitleLangKey, options.subtitleLangArgs)); @@ -109,45 +143,34 @@ export default class Row { if(options.title || options.titleLangKey) { let c: HTMLElement; - const titleRight = options.titleRight || options.titleRightSecondary; - if(titleRight) { - c = document.createElement('div'); - c.classList.add('row-title-row'); - this.container.append(c); + const titleRightContent = options.titleRight || options.titleRightSecondary; + if(titleRightContent) { + this.container.append(c = this.titleRow = this.createRow()); + this.titleRow.classList.add('row-title-row'); } else { c = this.container; } - this.title = document.createElement('div'); - this.title.classList.add('row-title'); - this.title.setAttribute('dir', 'auto'); + this.title = this.createTitle(); if(options.noWrap) this.title.classList.add('no-wrap'); if(options.title) { - if(typeof(options.title) === 'string') { - this.title.innerHTML = options.title; - } else { - this.title.append(options.title); - } - } else { + setContent(this.title, options.title); + } else if(options.titleLangKey) { this.title.append(i18n(options.titleLangKey)); } + c.append(this.title); - if(titleRight) { - const titleRightEl = this.titleRight = document.createElement('div'); - titleRightEl.classList.add('row-title', 'row-title-right'); + if(titleRightContent) { + const titleRight = this.titleRight = document.createElement('div'); + titleRight.classList.add('row-title', 'row-title-right'); if(options.titleRightSecondary) { - titleRightEl.classList.add('row-title-right-secondary'); + titleRight.classList.add('row-title-right-secondary'); } - if(typeof(titleRight) === 'string') { - titleRightEl.innerHTML = titleRight; - } else { - titleRightEl.append(titleRight); - } - - c.append(titleRightEl); + setContent(titleRight, titleRightContent); + c.append(titleRight); } } @@ -196,6 +219,19 @@ export default class Row { return this._subtitle ?? (this._subtitle = this.createSubtitle()); } + private createRow() { + const c = document.createElement('div'); + c.classList.add('row-row'); + return c; + } + + private createTitle() { + const title = document.createElement('div'); + title.classList.add('row-title'); + title.setAttribute('dir', 'auto'); + return title; + } + private createSubtitle() { const subtitle = document.createElement('div'); subtitle.classList.add('row-subtitle'); @@ -206,10 +242,15 @@ export default class Row { return subtitle; } - public createMedia(size?: 'small') { + public createMedia(size?: RowMediaSizeType) { + const media = document.createElement('div'); + return this.applyMediaElement(media, size); + } + + public applyMediaElement(media: HTMLElement, size?: RowMediaSizeType) { this.container.classList.add('row-with-padding'); - const media = this.media = document.createElement('div'); + this.media = media; media.classList.add('row-media'); if(size) { diff --git a/src/components/sidebarLeft/tabs/addMembers.ts b/src/components/sidebarLeft/tabs/addMembers.ts index f74ade178..9436439e1 100644 --- a/src/components/sidebarLeft/tabs/addMembers.ts +++ b/src/components/sidebarLeft/tabs/addMembers.ts @@ -59,7 +59,8 @@ export default class AppAddMembersTab extends SliderSuperTab { placeholder: options.placeholder, exceptSelf: isPrivacy, filterPeerTypeBy: isPrivacy ? ['isAnyGroup', 'isUser'] : undefined, - managers: this.managers + managers: this.managers, + design: 'square' }); if(options.selectedPeerIds) { diff --git a/src/components/sidebarLeft/tabs/blockedUsers.ts b/src/components/sidebarLeft/tabs/blockedUsers.ts index 44768e017..d496035a1 100644 --- a/src/components/sidebarLeft/tabs/blockedUsers.ts +++ b/src/components/sidebarLeft/tabs/blockedUsers.ts @@ -59,7 +59,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab { peerId: peerId, container: list, rippleEnabled: true, - avatarSize: 48, + avatarSize: 'abitbigger', append }); diff --git a/src/components/sidebarLeft/tabs/contacts.ts b/src/components/sidebarLeft/tabs/contacts.ts index 90cd9bc74..6bc5f2e85 100644 --- a/src/components/sidebarLeft/tabs/contacts.ts +++ b/src/components/sidebarLeft/tabs/contacts.ts @@ -75,7 +75,7 @@ export default class AppContactsTab extends SliderSuperTab { protected onClose() { this.middleware.clean(); /* // need to clear, and left 1 page for smooth slide - let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; + let pageCount = appPhotosManager.windowH / 56 * 1.25 | 0; (Array.from(this.list.children) as HTMLElement[]).slice(pageCount).forEach((el) => el.remove()); */ } @@ -98,7 +98,7 @@ export default class AppContactsTab extends SliderSuperTab { const sortedUserList = this.sortedUserList = this.createList(); let renderPage = () => { - const pageCount = windowSize.height / 72 * 1.25 | 0; + const pageCount = windowSize.height / 56 * 1.25 | 0; const arr = contacts.splice(0, pageCount); // надо splice! arr.forEach((peerId) => { diff --git a/src/components/sidebarLeft/tabs/editFolder.ts b/src/components/sidebarLeft/tabs/editFolder.ts index d771521a5..172ffb36e 100644 --- a/src/components/sidebarLeft/tabs/editFolder.ts +++ b/src/components/sidebarLeft/tabs/editFolder.ts @@ -317,7 +317,7 @@ export default class AppEditFolderTab extends SliderSuperTab { container: ul, rippleEnabled: false, meAsSaved: true, - avatarSize: 32 + avatarSize: 'small' }); dom.lastMessageSpan.parentElement.remove(); } diff --git a/src/components/sidebarLeft/tabs/editProfile.ts b/src/components/sidebarLeft/tabs/editProfile.ts index 8e7192775..4c3e4eeaf 100644 --- a/src/components/sidebarLeft/tabs/editProfile.ts +++ b/src/components/sidebarLeft/tabs/editProfile.ts @@ -12,6 +12,7 @@ import {i18n, i18n_} from '../../../lib/langPack'; import {attachClickEvent} from '../../../helpers/dom/clickEvent'; import rootScope from '../../../lib/rootScope'; import {generateSection, SettingSection} from '..'; +import anchorCopy from '../../../helpers/dom/anchorCopy'; // TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист) @@ -114,10 +115,7 @@ export default class AppEditProfileTab extends SliderSuperTab { const profileUrlContainer = this.profileUrlContainer = document.createElement('div'); profileUrlContainer.classList.add('profile-url-container'); - const profileUrlAnchor = this.profileUrlAnchor = document.createElement('a'); - profileUrlAnchor.classList.add('profile-url'); - profileUrlAnchor.href = '#'; - profileUrlAnchor.target = '_blank'; + const profileUrlAnchor = this.profileUrlAnchor = anchorCopy(); profileUrlContainer.append(i18n('UsernameHelpLink', [profileUrlAnchor])); @@ -173,9 +171,7 @@ export default class AppEditProfileTab extends SliderSuperTab { this.profileUrlContainer.style.display = 'none'; } else { this.profileUrlContainer.style.display = ''; - const url = 'https://t.me/' + this.usernameInputField.value; - this.profileUrlAnchor.innerText = url; - this.profileUrlAnchor.href = url; + this.profileUrlAnchor.replaceWith(this.profileUrlAnchor = anchorCopy({mePath: this.usernameInputField.value})); } } } diff --git a/src/components/sidebarLeft/tabs/includedChats.ts b/src/components/sidebarLeft/tabs/includedChats.ts index ad49e68ce..965e7c65d 100644 --- a/src/components/sidebarLeft/tabs/includedChats.ts +++ b/src/components/sidebarLeft/tabs/includedChats.ts @@ -163,7 +163,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab { peerId: peerId, container: this.selector.scrollable, rippleEnabled: true, - avatarSize: 46 + avatarSize: 'abitbigger' }); const selected = this.selector.selected.has(peerId); diff --git a/src/components/sidebarLeft/tabs/newGroup.ts b/src/components/sidebarLeft/tabs/newGroup.ts index f9c971dd5..abc0e312c 100644 --- a/src/components/sidebarLeft/tabs/newGroup.ts +++ b/src/components/sidebarLeft/tabs/newGroup.ts @@ -166,7 +166,7 @@ export default class AppNewGroupTab extends SliderSuperTab { peerId: userId, container: this.list, rippleEnabled: false, - avatarSize: 48 + avatarSize: 'abitbigger' }); dom.lastMessageSpan.append(getUserStatusString(await this.managers.appUsersManager.getUser(userId))); diff --git a/src/components/sidebarRight/index.ts b/src/components/sidebarRight/index.ts index 592ff03c5..30d0ee020 100644 --- a/src/components/sidebarRight/index.ts +++ b/src/components/sidebarRight/index.ts @@ -52,7 +52,7 @@ export class AppSidebarRight extends SidebarSlider { const idx = this.historyTabIds.indexOf(previousTab); if(this._selectTab.getFrom() === previousTab.container) { - this._selectTab.setFrom(tab.container); + this._selectTab.setFrom(tab?.container); } if(tab) { diff --git a/src/components/sidebarRight/tabs/groupPermissions.ts b/src/components/sidebarRight/tabs/groupPermissions.ts index 1ee57b343..7d7d69fae 100644 --- a/src/components/sidebarRight/tabs/groupPermissions.ts +++ b/src/components/sidebarRight/tabs/groupPermissions.ts @@ -264,7 +264,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { peerId: getPeerId(participant.peer), container: list, rippleEnabled: true, - avatarSize: 48, + avatarSize: 'abitbigger', append }); diff --git a/src/components/sidebarRight/tabs/pollResults.ts b/src/components/sidebarRight/tabs/pollResults.ts index 2772bfbb2..3e9b0b033 100644 --- a/src/components/sidebarRight/tabs/pollResults.ts +++ b/src/components/sidebarRight/tabs/pollResults.ts @@ -8,15 +8,16 @@ import {SliderSuperTab} from '../../slider'; import appSidebarRight from '..'; import {roundPercents} from '../../poll'; import appDialogsManager from '../../../lib/appManagers/appDialogsManager'; -import ripple from '../../ripple'; import {i18n} from '../../../lib/langPack'; import setInnerHTML from '../../../helpers/dom/setInnerHTML'; import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText'; +import Button from '../../button'; +import {Message, MessageMedia} from '../../../layer'; export default class AppPollResultsTab extends SliderSuperTab { private resultsDiv: HTMLElement; - public async init(message: any) { + public async init(message: Message.message) { this.container.id = 'poll-results-container'; this.container.classList.add('chatlist-container'); @@ -24,7 +25,7 @@ export default class AppPollResultsTab extends SliderSuperTab { this.resultsDiv.classList.add('poll-results'); this.scrollable.append(this.resultsDiv); - const poll = await this.managers.appPollsManager.getPoll(message.media.poll.id); + const poll = await this.managers.appPollsManager.getPoll((message.media as MessageMedia.messageMediaPoll).poll.id); this.setTitle(poll.poll.pFlags.quiz ? 'PollResults.Title.Quiz' : 'PollResults.Title.Poll'); @@ -62,11 +63,11 @@ export default class AppPollResultsTab extends SliderSuperTab { appSidebarRight.onCloseBtnClick(); }, undefined, true); - list.style.minHeight = Math.min(result.voters, 4) * 50 + 'px'; + list.style.minHeight = Math.min(result.voters, 4) * 48 + 'px'; fragment.append(hr, answerEl, list); - let offset: string, limit = 4, loading = false, left = result.voters - 4; + let offset: string, limit = 4, loading = false, left = Math.max(0, result.voters - 4); const load = () => { if(loading) return; loading = true; @@ -78,14 +79,17 @@ export default class AppPollResultsTab extends SliderSuperTab { container: list, rippleEnabled: false, meAsSaved: false, - avatarSize: 32 + avatarSize: 'small' }); dom.lastMessageSpan.parentElement.remove(); }); if(offset) { - left -= votesList.votes.length; - (showMore.lastElementChild as HTMLElement).replaceWith(i18n('PollResults.LoadMore', [Math.min(20, left)])); + left = Math.max(0, left - votesList.votes.length); + + if(left) { + (showMore.lastElementChild as HTMLElement).replaceWith(i18n('PollResults.LoadMore', [Math.min(20, left)])); + } } offset = votesList.next_offset; @@ -99,19 +103,13 @@ export default class AppPollResultsTab extends SliderSuperTab { }); }; - load(); - - if(left <= 0) return; - - const showMore = document.createElement('div'); - showMore.classList.add('poll-results-more', 'show-more', 'rp-overflow'); + const showMore = Button('poll-results-more btn btn-primary btn-transparent', {icon: 'down'}); showMore.addEventListener('click', load); - ripple(showMore); - const down = document.createElement('div'); - down.classList.add('tgico-down'); - showMore.append(down, i18n('PollResults.LoadMore', [Math.min(20, left)])); + showMore.append(i18n('PollResults.LoadMore', [Math.min(20, left)])); fragment.append(showMore); + + load(); }); this.resultsDiv.append(title, fragment); diff --git a/src/components/sidebarRight/tabs/userPermissions.ts b/src/components/sidebarRight/tabs/userPermissions.ts index 4b5eb6b6a..ed5adfe79 100644 --- a/src/components/sidebarRight/tabs/userPermissions.ts +++ b/src/components/sidebarRight/tabs/userPermissions.ts @@ -42,7 +42,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { peerId: this.userId.toPeerId(false), container: list, rippleEnabled: true, - avatarSize: 48 + avatarSize: 'abitbigger' }); dom.lastMessageSpan.append(getUserStatusString(await this.managers.appUsersManager.getUser(this.userId))); diff --git a/src/components/sortedUserList.ts b/src/components/sortedUserList.ts index 776c7c96f..b2822bda6 100644 --- a/src/components/sortedUserList.ts +++ b/src/components/sortedUserList.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import appDialogsManager, {AppDialogsManager, DialogDom} from '../lib/appManagers/appDialogsManager'; +import appDialogsManager, {AppDialogsManager, DialogDom, DialogElementSize} from '../lib/appManagers/appDialogsManager'; import {getHeavyAnimationPromise} from '../hooks/useHeavyAnimationCheck'; import isInDOM from '../helpers/dom/isInDOM'; import positionElementByIndex from '../helpers/dom/positionElementByIndex'; @@ -25,7 +25,7 @@ export default class SortedUserList extends SortedList { public list: HTMLUListElement; protected lazyLoadQueue: LazyLoadQueue; - protected avatarSize = 48; + protected avatarSize: DialogElementSize = 'abitbigger'; protected rippleEnabled = true; protected autonomous = true; protected createChatListOptions: Parameters[0]; diff --git a/src/components/wrappers/messageActionTextNewUnsafe.ts b/src/components/wrappers/messageActionTextNewUnsafe.ts index 768bbaadd..1eca561cb 100644 --- a/src/components/wrappers/messageActionTextNewUnsafe.ts +++ b/src/components/wrappers/messageActionTextNewUnsafe.ts @@ -5,7 +5,7 @@ */ import indexOfAndSplice from '../../helpers/array/indexOfAndSplice'; -import {formatTime} from '../../helpers/date'; +import {formatTime, ONE_DAY} from '../../helpers/date'; import htmlToSpan from '../../helpers/dom/htmlToSpan'; import setInnerHTML from '../../helpers/dom/setInnerHTML'; import formatCallDuration from '../../helpers/formatCallDuration'; @@ -18,16 +18,21 @@ import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText'; import wrapPlainText from '../../lib/richTextProcessor/wrapPlainText'; import wrapRichText from '../../lib/richTextProcessor/wrapRichText'; import rootScope from '../../lib/rootScope'; -import PeerTitle from '../peerTitle'; import getPeerTitle from './getPeerTitle'; import wrapJoinVoiceChatAnchor from './joinVoiceChatAnchor'; import wrapMessageForReply from './messageForReply'; +import wrapPeerTitle from './peerTitle'; async function wrapLinkToMessage(message: Message.message | Message.messageService, plain?: boolean) { + const wrapped = await wrapMessageForReply(message, undefined, undefined, plain as any); + if(plain) { + return wrapped; + } + const a = document.createElement('i'); a.dataset.savedFrom = message.peerId + '_' + message.mid; a.dir = 'auto'; - a.append(await wrapMessageForReply(message, undefined, undefined, plain as any)); + a.append(wrapped); return a; } @@ -53,8 +58,8 @@ export default async function wrapMessageActionTextNewUnsafe(message: MyMessage, const managers = rootScope.managers; - const getNameDivHTML = async(peerId: PeerId, plain: boolean) => { - return plain ? getPeerTitle(peerId, plain) : (new PeerTitle({peerId})).element; + const getNameDivHTML = (peerId: PeerId, plain: boolean) => { + return plain ? getPeerTitle(peerId, plain) : wrapPeerTitle({peerId}); }; switch(action._) { @@ -268,7 +273,10 @@ export default async function wrapMessageActionTextNewUnsafe(message: MyMessage, } else { langPackKey = isRecurringUsed ? 'Chat.Service.PaymentSentRecurringUsed' : (isRecurringInit ? 'Chat.Service.PaymentSentRecurringInit' : 'Chat.Service.PaymentSent1'); args.push(wrapLinkToMessage(invoiceMessage, plain).then((el) => { - el.classList.add('is-receipt-link'); + if(el instanceof HTMLElement) { + el.classList.add('is-receipt-link'); + } + return el; })); } @@ -277,6 +285,52 @@ export default async function wrapMessageActionTextNewUnsafe(message: MyMessage, break; } + case 'messageActionSetMessagesTTL': { + args = []; + + const isBroadcast = await managers.appPeersManager.isBroadcast(message.peerId); + if(action.period) { + if(isBroadcast) { + langPackKey = 'ActionTTLChannelChanged'; + } else if(message.fromId === rootScope.myId) { + langPackKey = 'ActionTTLYouChanged'; + } else { + langPackKey = 'ActionTTLChanged'; + args.push(getNameDivHTML(message.fromId, plain)); + } + + let duration: ReturnType; + if(action.period > 1814400) { + let key: LangPackKey; + const args: FormatterArguments = []; + const year = 31536000; + if(action.period >= year) { + key = 'Years'; + args.push(action.period / year | 0); + } else { + key = 'Months'; + args.push(action.period / (ONE_DAY * 30) | 0); + } + + duration = plain ? I18n.format(key, true, args) : i18n(key, args); + } else { + duration = formatCallDuration(action.period, plain); + } + + args.push(duration); + } else { + if(isBroadcast) { + langPackKey = 'ActionTTLChannelDisabled'; + } else if(message.fromId === rootScope.myId) { + langPackKey = 'ActionTTLYouDisabled'; + } else { + langPackKey = 'ActionTTLDisabled'; + args.push(getNameDivHTML(message.fromId, plain)); + } + } + break; + } + default: langPackKey = (langPack[_] || `[${action._}]`) as any; break; diff --git a/src/components/wrappers/messageForReply.ts b/src/components/wrappers/messageForReply.ts index 16a30c9e5..dc0d157a9 100644 --- a/src/components/wrappers/messageForReply.ts +++ b/src/components/wrappers/messageForReply.ts @@ -40,7 +40,7 @@ export default async function wrapMessageForReply(message: MyMessage | MyDraftMe if(plain) { parts.push(part); } else { - const el = document.createElement('i'); + const el = document.createElement('span'); if(typeof(part) === 'string') el.innerHTML = part; else el.append(part); parts.push(el); diff --git a/src/components/wrappers/sticker.ts b/src/components/wrappers/sticker.ts index 8c075b16b..20852e3f0 100644 --- a/src/components/wrappers/sticker.ts +++ b/src/components/wrappers/sticker.ts @@ -704,14 +704,48 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m peerId: PeerId, middleware: Middleware }) { - if(!peerId.isUser()) { - return; - } - cancelEvent(event); const bubble = findUpClassName(container, 'bubble'); const emoji = container.dataset.stickerEmoji; + + const animation = !container.classList.contains('custom-emoji') ? lottieLoader.getAnimation(container) : undefined; + if(animation?.paused) { + const doc = await managers.appStickersManager.getAnimatedEmojiSoundDocument(emoji); + if(doc) { + const audio = document.createElement('audio'); + audio.style.display = 'none'; + container.parentElement.append(audio); + + try { + const url = await appDownloadManager.downloadMediaURL({media: doc}); + + audio.src = url; + audio.play(); + await onMediaLoad(audio, undefined, true); + + audio.addEventListener('ended', () => { + audio.src = ''; + audio.remove(); + }, {once: true}); + } catch(err) { + + } + } + + animation.autoplay = true; + animation.restart(); + } + + if(!peerId.isUser()) { + return; + } + + const doc = await managers.appStickersManager.getAnimatedEmojiSticker(emoji, true); + if(!doc) { + return; + } + const data: SendMessageEmojiInteractionData = (container as any).emojiData ??= { a: [], v: 1 @@ -743,39 +777,6 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m data.a.length = 0; }, 1000, false); - const animation = !container.classList.contains('custom-emoji') ? lottieLoader.getAnimation(container) : undefined; - if(animation?.paused) { - const doc = await managers.appStickersManager.getAnimatedEmojiSoundDocument(emoji); - if(doc) { - const audio = document.createElement('audio'); - audio.style.display = 'none'; - container.parentElement.append(audio); - - try { - const url = await appDownloadManager.downloadMediaURL({media: doc}); - - audio.src = url; - audio.play(); - await onMediaLoad(audio, undefined, true); - - audio.addEventListener('ended', () => { - audio.src = ''; - audio.remove(); - }, {once: true}); - } catch(err) { - - } - } - - animation.autoplay = true; - animation.restart(); - } - - const doc = await managers.appStickersManager.getAnimatedEmojiSticker(emoji, true); - if(!doc) { - return; - } - const isOut = bubble ? bubble.classList.contains('is-out') : undefined; const {animationDiv} = wrapStickerAnimation({ doc, diff --git a/src/helpers/dom/superIntersectionObserver.ts b/src/helpers/dom/superIntersectionObserver.ts index f82d2e0ed..f099d387c 100644 --- a/src/helpers/dom/superIntersectionObserver.ts +++ b/src/helpers/dom/superIntersectionObserver.ts @@ -24,7 +24,9 @@ export default class SuperIntersectionObserver { const entry = entries[i]; const callbacks = observing.get(entry.target); if(!callbacks) { + console.error('intersection process no callbacks:', entry); debugger; + continue; } for(const callback of callbacks) { diff --git a/src/helpers/formatCallDuration.ts b/src/helpers/formatCallDuration.ts index 58872b53f..1ac17547a 100644 --- a/src/helpers/formatCallDuration.ts +++ b/src/helpers/formatCallDuration.ts @@ -12,7 +12,9 @@ const CALL_DURATION_LANG_KEYS: {[type in DurationType]: LangPackKey} = { m: 'Minutes', h: 'Hours', d: 'Days', - w: 'Weeks' + w: 'Weeks', + mm: 'Months', + y: 'Years' }; export default function formatCallDuration(duration: number, plain?: boolean) { const a = formatDuration(duration, 2); diff --git a/src/helpers/formatDuration.ts b/src/helpers/formatDuration.ts index bcb4a194b..017072e31 100644 --- a/src/helpers/formatDuration.ts +++ b/src/helpers/formatDuration.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -export type DurationType = 's' | 'm' | 'h' | 'd' | 'w'; +export type DurationType = 's' | 'm' | 'h' | 'd' | 'w' | 'mm' | 'y'; export default function formatDuration(duration: number, showLast = 2) { if(!duration) { duration = 1; @@ -21,7 +21,7 @@ export default function formatDuration(duration: number, showLast = 2) { const s = 1; let t = s; p.forEach((o, idx) => { - t *= o.m; + t = Math.round(t * o.m); if(duration < t) { return; diff --git a/src/lang.ts b/src/lang.ts index 5a0a61047..7f89ddfab 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -504,6 +504,14 @@ const lang = { 'one_value': '%1$d week', 'other_value': '%1$d weeks' }, + 'Months': { + 'one_value': '%1$d month', + 'other_value': '%1$d months' + }, + 'Years': { + 'one_value': '%1$d year', + 'other_value': '%1$d years' + }, 'TodayAtFormattedWithToday': 'today at %1$s', 'formatDateAtTime': '%1$s at %2$s', 'JoinByPeekChannelTitle': 'Join Channel', @@ -791,6 +799,12 @@ const lang = { 'IncreaseLimit': 'Increase Limit', 'LimitFree': 'Free', 'LimitPremium': 'Premium', + 'ActionTTLChanged': 'un1 set messages to auto-delete in %1$s', + 'ActionTTLYouChanged': 'You set messages to auto-delete in %1$s', + 'ActionTTLChannelChanged': 'Messages in this channel will be automatically deleted after %1$s', + 'ActionTTLChannelDisabled': 'Messages in this channel will no longer be automatically deleted', + 'ActionTTLDisabled': 'un1 disabled the auto-delete timer', + 'ActionTTLYouDisabled': 'You disabled the auto-delete timer', // * macos 'AccountSettings.Filters': 'Chat Folders', diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 237d97ff7..9f974bafc 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -12,7 +12,7 @@ import deepEqual from '../../helpers/object/deepEqual'; import isObject from '../../helpers/object/isObject'; import safeReplaceObject from '../../helpers/object/safeReplaceObject'; -import {ChannelParticipant, ChannelsCreateChannel, Chat, ChatAdminRights, ChatBannedRights, ChatInvite, ChatPhoto, ChatReactions, InputChannel, InputChatPhoto, InputFile, InputPeer, SponsoredMessage, Update, Updates} from '../../layer'; +import {ChannelParticipant, ChannelsCreateChannel, Chat, ChatAdminRights, ChatBannedRights, ChatInvite, ChatPhoto, ChatReactions, InputChannel, InputChatPhoto, InputFile, InputPeer, MessagesSponsoredMessages, SponsoredMessage, Update, Updates} from '../../layer'; import {isRestricted} from '../../helpers/restrictions'; import {AppManager} from './manager'; import hasRights from './utils/chats/hasRights'; @@ -694,9 +694,93 @@ export class AppChatsManager extends AppManager { } public getSponsoredMessage(chatId: ChatId) { - return this.apiManager.invokeApiCacheable('channels.getSponsoredMessages', { + // const s: MessagesSponsoredMessages.messagesSponsoredMessages = { + // '_': 'messages.sponsoredMessages', + // 'messages': [ + // { + // '_': 'sponsoredMessage', + // 'pFlags': {}, + // 'flags': 9, + // 'random_id': new Uint8Array([ + // 80, + // 5, + // 249, + // 174, + // 44, + // 73, + // 173, + // 14, + // 246, + // 81, + // 187, + // 182, + // 223, + // 5, + // 4, + // 128 + // ]), + // 'from_id': { + // '_': 'peerUser', + // 'user_id': 983000232 + // }, + // 'start_param': 'GreatMinds', + // 'message': 'This is a long sponsored message. In fact, it has the maximum length allowed on the platform – 160 characters 😬😬. It\'s promoting a bot with a start parameter.' + chatId + // } + // ], + // 'chats': [], + // 'users': [ + // { + // '_': 'user', + // 'pFlags': { + // 'bot': true, + // 'verified': true, + // 'apply_min_photo': true + // }, + // 'flags': 34226219, + // 'id': 983000232, + // 'access_hash': '-294959558742535650', + // 'first_name': 'Quiz Bot', + // 'username': 'QuizBot', + // 'photo': { + // '_': 'userProfilePhoto', + // 'pFlags': {}, + // 'flags': 2, + // 'photo_id': '4221953848856651689', + // 'stripped_thumb': new Uint8Array([ + // 1, + // 8, + // 8, + // 155, + // 247, + // 95, + // 103, + // 255, + // 0, + // 110, + // 138, + // 40, + // 174, + // 132, + // 142, + // 6, + // 238, + // 127 + // ]), + // 'dc_id': 2 + // }, + // 'bot_info_version': 11, + // 'bot_inline_placeholder': 'Search a quiz...', + // 'sortName': 'quiz bot' + // } + // ] + // }; + + // const promise = Promise.resolve(s); + const promise = this.apiManager.invokeApiCacheable('channels.getSponsoredMessages', { channel: this.getChannelInput(chatId) - }, {cacheSeconds: 300}).then((sponsoredMessages) => { + }, {cacheSeconds: 300}); + + return promise.then((sponsoredMessages) => { this.appUsersManager.saveApiUsers(sponsoredMessages.users); this.appChatsManager.saveApiChats(sponsoredMessages.chats); diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index a20769dc6..b25d24712 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -86,12 +86,13 @@ import whichChild from '../../helpers/dom/whichChild'; import {MiddlewareHelper} from '../../helpers/middleware'; import makeError from '../../helpers/makeError'; import getUnsafeRandomInt from '../../helpers/number/getUnsafeRandomInt'; +import Row, {RowMediaSizeType} from '../../components/row' export const DIALOG_LIST_ELEMENT_TAG = 'A'; export type DialogDom = { avatarEl: AvatarElement, - captionDiv: HTMLDivElement, + captionDiv: HTMLElement, titleSpan: HTMLSpanElement, titleSpanContainer: HTMLSpanElement, statusSpan: HTMLSpanElement, @@ -128,6 +129,8 @@ function setPromiseMiddleware { constructor( public managers: AppManagers, @@ -170,6 +173,151 @@ class SortedDialogList extends SortedList { } } +export type DialogElementSize = RowMediaSizeType; +class DialogElement extends Row { + public dom: DialogDom; + + constructor({ + peerId, + rippleEnabled = true, + onlyFirstName = false, + meAsSaved = true, + avatarSize = 'bigger', + autonomous, + lazyLoadQueue, + loadPromises, + fromName, + noIcons + }: { + peerId: PeerId, + rippleEnabled?: boolean, + onlyFirstName?: boolean, + meAsSaved?: boolean, + avatarSize?: RowMediaSizeType, + autonomous?: boolean, + lazyLoadQueue?: LazyLoadQueue, + loadPromises?: Promise[], + fromName?: string, + noIcons?: boolean + }) { + super({ + clickable: true, + noRipple: !rippleEnabled, + havePadding: true, + title: true, + titleRightSecondary: true, + subtitle: true, + subtitleRight: true, + noWrap: true, + asLink: true + }); + + const avatarEl = new AvatarElement(); + const avatarSizeMap: {[k in DialogElementSize]?: number} = { + bigger: 54, + abitbigger: 42, + small: 32 + }; + const s = avatarSizeMap[avatarSize]; + avatarEl.classList.add('dialog-avatar', 'avatar-' + s); + avatarEl.updateWithOptions({ + loadPromises, + lazyLoadQueue, + isDialog: !!meAsSaved, + peerId, + peerTitle: fromName + }); + + const captionDiv = this.container; + + const titleSpanContainer = this.title; + titleSpanContainer.classList.add('user-title'); + + this.titleRow.classList.add('dialog-title'); + + const peerTitle = new PeerTitle(); + const peerTitlePromise = peerTitle.update({ + peerId, + fromName, + dialog: meAsSaved, + onlyFirstName, + plainText: false, + withIcons: !noIcons + }); + + loadPromises?.push(peerTitlePromise); + titleSpanContainer.append(peerTitle.element); + + // for muted icon + titleSpanContainer.classList.add('tgico'); // * эта строка будет актуальна только для !container, но ладно + + // const titleIconsPromise = generateTitleIcons(peerId).then((elements) => { + // titleSpanContainer.append(...elements); + // }); + + // if(loadPromises) { + // loadPromises.push(titleIconsPromise); + // } + // } + + const span = this.subtitle; + // span.classList.add('user-last-message'); + + const li = this.container; + li.classList.add('chatlist-chat', 'chatlist-chat-' + avatarSize); + if(!autonomous) (li as HTMLAnchorElement).href = '#' + peerId; + // if(rippleEnabled) { + // ripple(li); + // } + + if(avatarSize === 'bigger') { + this.container.classList.add('row-big'); + } else if(avatarSize === 'small') { + this.container.classList.add('row-small'); + } + + this.applyMediaElement(avatarEl, avatarSize); + li.dataset.peerId = '' + peerId; + + const statusSpan = document.createElement('span'); + statusSpan.classList.add('message-status', 'sending-status'/* , 'transition', 'reveal' */); + + const lastTimeSpan = document.createElement('span'); + lastTimeSpan.classList.add('message-time'); + + const unreadBadge = document.createElement('div'); + unreadBadge.className = 'dialog-subtitle-badge badge badge-' + BADGE_SIZE; + + const rightSpan = this.titleRight; + rightSpan.classList.add('dialog-title-details'); + rightSpan.append(statusSpan, lastTimeSpan); + + this.subtitleRow.classList.add('dialog-subtitle'); + + const dom: DialogDom = this.dom = { + avatarEl, + captionDiv, + titleSpan: peerTitle.element, + titleSpanContainer, + statusSpan, + lastTimeSpan, + unreadBadge, + lastMessageSpan: span, + containerEl: li, + listEl: li, + subtitleEl: this.subtitleRow + }; + + if(!autonomous) { + (li as any).dialogDom = dom; + + if(appImManager.chat?.peerId === peerId) { + appDialogsManager.setDialogActive(li, true); + } + } + } +} + // const testScroll = false; // let testTopSlice = 1; @@ -316,6 +464,7 @@ export class AppDialogsManager { // setTimeout(() => apiManagerProxy.getState().then(async(state) => { this.loadedDialogsAtLeastOnce = false; + this.showFiltersPromise = undefined; /* const clearPromises: Promise[] = []; for(const name in this.managers.appStateManager.storagesResults) { @@ -659,9 +808,8 @@ export class AppDialogsManager { }); } - private setDialogActive(listEl: HTMLElement, active: boolean) { - // @ts-ignore - const dom = listEl.dialogDom as DialogDom; + public setDialogActive(listEl: HTMLElement, active: boolean) { + const dom = (listEl as any).dialogDom as DialogDom; listEl.classList.toggle('active', active); if(active) { this.lastActiveElements.add(listEl); @@ -955,31 +1103,29 @@ export class AppDialogsManager { } private onFiltersLengthChange() { - if(!this.showFiltersPromise) { - this.showFiltersPromise = new Promise((resolve) => { - window.setTimeout(() => { - const length = Object.keys(this.filtersRendered).length; - const show = length > 1; - const wasShowing = !this.folders.menuScrollContainer.classList.contains('hide'); + let promise = this.showFiltersPromise; + return promise ??= this.showFiltersPromise = pause(0).then(() => { + if(this.showFiltersPromise !== promise) { + return; + } - if(show !== wasShowing) { - this.folders.menuScrollContainer.classList.toggle('hide', !show); - if(show && !wasShowing) { - this.setFiltersUnreadCount(); - } + const length = Object.keys(this.filtersRendered).length; + const show = length > 1; + const wasShowing = !this.folders.menuScrollContainer.classList.contains('hide'); - this.chatsContainer.classList.toggle('has-filters', show); - } + if(show !== wasShowing) { + this.folders.menuScrollContainer.classList.toggle('hide', !show); + if(show && !wasShowing) { + this.setFiltersUnreadCount(); + } - this.changeFiltersAllChatsKey(); + this.chatsContainer.classList.toggle('has-filters', show); + } - this.showFiltersPromise = undefined; - resolve(); - }, 0); - }); - } + this.changeFiltersAllChatsKey(); - return this.showFiltersPromise; + this.showFiltersPromise = undefined; + }); } private loadDialogs(side: SliceSides) { @@ -1341,7 +1487,7 @@ export class AppDialogsManager { }; const sortedUserList = new SortedUserList({ - avatarSize: 42, + avatarSize: 'abitbigger', createChatListOptions: { dialogSize: 48, new: true @@ -1737,30 +1883,31 @@ export class AppDialogsManager { /* if(lastMessage.from_id === auth.id) { // You: */ if(draftMessage) { - const bold = document.createElement('b'); - bold.classList.add('danger'); - bold.append(i18n('Draft'), ': '); - willPrepend.unshift(bold); + const span = document.createElement('span'); + span.classList.add('danger'); + span.append(i18n('Draft'), ': '); + willPrepend.unshift(span); } else if(peerId.isAnyChat() && peerId !== lastMessage.fromId && !(lastMessage as Message.messageService).action) { - const senderBold = document.createElement('b'); + const span = document.createElement('span'); + span.classList.add('primary-text'); if(lastMessage.fromId === rootScope.myId) { - senderBold.append(i18n('FromYou')); - willPrepend.unshift(senderBold); + span.append(i18n('FromYou')); + willPrepend.unshift(span); } else { // str = sender.first_name || sender.last_name || sender.username; const p = middleware(wrapPeerTitle({ peerId: lastMessage.fromId, onlyFirstName: true })).then((element) => { - senderBold.prepend(element); - return senderBold; + span.prepend(element); + return span; }, noop); willPrepend.unshift(p); } - senderBold.append(': '); + span.append(': '); // console.log(sender, senderBold.innerText); } @@ -1870,7 +2017,7 @@ export class AppDialogsManager { if(hasMentionsBadge) { if(!dom.mentionsBadge) { dom.mentionsBadge = document.createElement('div'); - dom.mentionsBadge.className = 'dialog-subtitle-badge badge badge-24 mention mention-badge'; + dom.mentionsBadge.className = `dialog-subtitle-badge badge badge-${BADGE_SIZE} mention mention-badge`; dom.mentionsBadge.innerText = '@'; dom.subtitleEl.insertBefore(dom.mentionsBadge, dom.lastMessageSpan.nextSibling); } @@ -2041,14 +2188,27 @@ export class AppDialogsManager { onlyFirstName?: boolean, meAsSaved?: boolean, append?: boolean, - avatarSize?: number, + avatarSize?: RowMediaSizeType, autonomous?: boolean, lazyLoadQueue?: LazyLoadQueue, loadPromises?: Promise[], fromName?: string, noIcons?: boolean }) { - return this.addDialog(options.peerId, options.container, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue, options.loadPromises, options.fromName, options.noIcons); + const d = new DialogElement({ + autonomous: !!options.container, + avatarSize: 'bigger', + ...options + // avatarSize: !options.avatarSize || options.avatarSize >= 54 ? 'bigger' : 'abitbigger', + }); + + if(options.container) { + const method = !options.append ? 'append' : 'prepend'; + options.container[method](d.container); + } + + return d; + // return this.addDialog(options.peerId, options.container, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue, options.loadPromises, options.fromName, options.noIcons); } public addDialog( @@ -2138,7 +2298,7 @@ export class AppDialogsManager { lastTimeSpan.classList.add('message-time'); const unreadBadge = document.createElement('div'); - unreadBadge.className = 'dialog-subtitle-badge badge badge-24'; + unreadBadge.className = 'dialog-subtitle-badge badge badge-' + BADGE_SIZE; const titleP = document.createElement('p'); titleP.classList.add('dialog-title'); diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index e10d0305f..4584a644d 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -1000,6 +1000,14 @@ export class AppImManager extends EventListenerBase<{ } private onHashChange = (saveState?: boolean) => { + try { + this.onHashChangeUnsafe(saveState); + } catch(err) { + this.log.error('hash change error', err); + } + }; + + private onHashChangeUnsafe = (saveState?: boolean) => { const hash = location.hash; if(!saveState) { appNavigationController.replaceState(); @@ -1023,6 +1031,10 @@ export class AppImManager extends EventListenerBase<{ } case '#/im': { + if(!Object.keys(params).length) { + break; + } + const p: string = params.p; const postId = params.post !== undefined ? generateMessageId(+params.post) : undefined; @@ -1289,7 +1301,7 @@ export class AppImManager extends EventListenerBase<{ const top = chatBubbles.scrollable.scrollTop; const position = { - mids: getObjectKeysAndSort(chatBubbles.bubbles, 'desc').filter((mid) => !chatBubbles.skippedMids.has(mid)), + mids: getObjectKeysAndSort(chatBubbles.bubbles, 'desc').filter((mid) => mid > 0 && !chatBubbles.skippedMids.has(mid)), top }; @@ -1704,9 +1716,7 @@ export class AppImManager extends EventListenerBase<{ this.dispatchEvent('peer_changed', chatTo.peerId); const searchTab = appSidebarRight.getTab(AppPrivateSearchTab); - if(searchTab) { - searchTab.close(); - } + searchTab?.close(); appSidebarRight.replaceSharedMediaTab(chatTo.sharedMediaTab); } diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 8328260b4..5fd1911c2 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -1492,6 +1492,8 @@ export class AppMessagesManager extends AppManager { sequential: options.sequential }; + this.pendingTopMsgs[peerId] = messageId; + if(!options.isGroupedItem && message.send) { callbacks.push(() => { if(options.clearDraft) { diff --git a/src/lib/appManagers/appPollsManager.ts b/src/lib/appManagers/appPollsManager.ts index 0432722ba..5896786d1 100644 --- a/src/lib/appManagers/appPollsManager.ts +++ b/src/lib/appManagers/appPollsManager.ts @@ -11,10 +11,12 @@ import parseMarkdown from '../richTextProcessor/parseMarkdown'; import {AppManager} from './manager'; import getServerMessageId from './utils/messageId/getServerMessageId'; +type PollId = Poll['id']; + export class AppPollsManager extends AppManager { - public polls: {[id: string]: Poll} = {}; - public results: {[id: string]: PollResults} = {}; - public pollToMessages: {[id: string]: Set} = {}; + public polls: {[id: PollId]: Poll} = {}; + public results: {[id: PollId]: PollResults} = {}; + public pollToMessages: {[id: PollId]: Set} = {}; private log = logger('POLLS', LogTypes.Error); @@ -78,7 +80,7 @@ export class AppPollsManager extends AppManager { return results; } - public getPoll(pollId: string): {poll: Poll, results: PollResults} { + public getPoll(pollId: PollId): {poll: Poll, results: PollResults} { return { poll: this.polls[pollId], results: this.results[pollId] @@ -128,8 +130,8 @@ export class AppPollsManager extends AppManager { } } - public sendVote(message: any, optionIds: number[]): Promise { - const poll: Poll = message.media.poll; + public sendVote(message: Message.message, optionIds: number[]): Promise { + const poll: Poll = (message.media as MessageMedia.messageMediaPoll).poll; const options: Uint8Array[] = optionIds.map((index) => { return poll.answers[index].option; @@ -142,7 +144,7 @@ export class AppPollsManager extends AppManager { if(message.pFlags.is_outgoing) { return this.appMessagesManager.invokeAfterMessageIsSent(messageId, 'sendVote', (message) => { this.log('invoke sendVote callback'); - return this.sendVote(message, optionIds); + return this.sendVote(message as Message.message, optionIds); }); } @@ -156,7 +158,7 @@ export class AppPollsManager extends AppManager { }); } - public getResults(message: any) { + public getResults(message: Message.message) { const inputPeer = this.appPeersManager.getInputPeerById(message.peerId); return this.apiManager.invokeApi('messages.getPollResults', { @@ -168,7 +170,7 @@ export class AppPollsManager extends AppManager { }); } - public getVotes(message: any, option?: Uint8Array, offset?: string, limit = 20) { + public getVotes(message: Message.message, option?: Uint8Array, offset?: string, limit = 20) { return this.apiManager.invokeApi('messages.getPollVotes', { peer: this.appPeersManager.getInputPeerById(message.peerId), id: getServerMessageId(message.mid), @@ -184,8 +186,8 @@ export class AppPollsManager extends AppManager { }); } - public stopPoll(message: any) { - const poll: Poll = message.media.poll; + public stopPoll(message: Message.message) { + const poll: Poll = (message.media as MessageMedia.messageMediaPoll).poll; if(poll.pFlags.closed) return Promise.resolve(); diff --git a/src/lib/appManagers/appProfileManager.ts b/src/lib/appManagers/appProfileManager.ts index 67a0f6764..6fa353fa1 100644 --- a/src/lib/appManagers/appProfileManager.ts +++ b/src/lib/appManagers/appProfileManager.ts @@ -271,8 +271,11 @@ export class AppProfileManager extends AppManager { const fullChat = this.chatsFull[id] as ChatFull.chatFull; if(fullChat && !override && Date.now() < this.fullExpiration[peerId]) { const chat: Chat.chat = this.appChatsManager.getChat(id); - if(chat.version === (fullChat.participants as ChatParticipants.chatParticipants).version || - chat.pFlags.left) { + if( + chat.pFlags.left || + chat.pFlags.deactivated || + chat.version === (fullChat.participants as ChatParticipants.chatParticipants).version + ) { return fullChat as ChatFull; } } diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index c63c81489..7f86d36b9 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -396,10 +396,16 @@ export class AppUsersManager extends AppManager { public getContactsPeerIds( query?: Parameters[0], includeSaved?: Parameters[1], - sortBy?: Parameters[2] + sortBy?: Parameters[2], + limit?: number ) { return this.getContacts(query, includeSaved, sortBy).then((userIds) => { - return userIds.map((userId) => userId.toPeerId(false)); + const peerIds = userIds.map((userId) => userId.toPeerId(false)); + if(limit) { + return peerIds.slice(0, limit); + } + + return peerIds; }); } diff --git a/src/lib/appManagers/getProxiedManagers.ts b/src/lib/appManagers/getProxiedManagers.ts index b4b31ffdd..85f0e5283 100644 --- a/src/lib/appManagers/getProxiedManagers.ts +++ b/src/lib/appManagers/getProxiedManagers.ts @@ -10,6 +10,7 @@ import apiManagerProxy from '../mtproto/mtprotoworker'; import {AckedResult} from '../mtproto/superMessagePort'; import noop from '../../helpers/noop'; import dT from '../../helpers/dT'; +import DEBUG from '../../config/debug'; // let stats: { // [manager: string]: { @@ -58,6 +59,11 @@ import dT from '../../helpers/dT'; // sentMethods2 = {}; // }, 2000); +const DEBUG_MANAGER_REQUESTS: {[managerName: string]: Set} = {}; +if(DEBUG) { + (window as any).DEBUG_MANAGER_REQUESTS = DEBUG_MANAGER_REQUESTS; +} + function createProxy(/* source: T, */name: string, ack?: boolean) { const proxy = new Proxy({}, { get: (target, p, receiver) => { @@ -75,6 +81,12 @@ function createProxy(/* source: T, */name: string, ack?: boolean) { args }, ack as any); + if(DEBUG) { + if(DEBUG_MANAGER_REQUESTS[name]?.has(p as any)) { + console.warn('manager request', name, p, args); + } + } + // collectStats(name, p as string, args, promise); return promise; @@ -93,27 +105,30 @@ type AA = { }; type T = Awaited>; -export default function getProxiedManagers() { - let proxied: { - [name in keyof T]?: ModifyFunctionsToAsync; - } & { - acknowledged?: { - [name in keyof T]?: AA; - } - }; +type ProxiedManagers = { + [name in keyof T]?: ModifyFunctionsToAsync; +} & { + acknowledged?: { + [name in keyof T]?: AA; + } +}; - function createProxyProxy(proxied: any, ack?: boolean) { - return new Proxy(proxied, { - get: (target, p, receiver) => { - // @ts-ignore - return target[p] ??= createProxy(p as string, ack); - } - }); +function createProxyProxy(proxied: any, ack?: boolean) { + return new Proxy(proxied, { + get: (target, p, receiver) => { + // @ts-ignore + return target[p] ??= createProxy(p as string, ack); + } + }); +} + +let proxied: ProxiedManagers; +export default function getProxiedManagers() { + if(proxied) { + return proxied; } proxied = createProxyProxy({}, false); - proxied.acknowledged = createProxyProxy({}, true); - return proxied; } diff --git a/src/lib/storages/dialogs.ts b/src/lib/storages/dialogs.ts index fcef7c819..947e654ce 100644 --- a/src/lib/storages/dialogs.ts +++ b/src/lib/storages/dialogs.ts @@ -795,14 +795,15 @@ export default class DialogsStorage extends AppManager { const updatedDialogs: Map = new Map(); (dialogsResult.dialogs as Dialog[]).forEach((dialog) => { const peerId = this.appPeersManager.getPeerId(dialog.peer); - let topMessage = dialog.top_message; + let topMid = dialog.top_message; - const topPendingMessage = this.appMessagesManager.pendingTopMsgs[peerId]; - if(topPendingMessage) { - if(!topMessage || - (this.appMessagesManager.getMessageByPeer(peerId, topPendingMessage) as MyMessage)?.date > (this.appMessagesManager.getMessageByPeer(peerId, topMessage) as MyMessage)?.date) { - dialog.top_message = topMessage = topPendingMessage; - this.appMessagesManager.getHistoryStorage(peerId).maxId = topPendingMessage; + const topPendingMid = this.appMessagesManager.pendingTopMsgs[peerId]; + if(topPendingMid) { + const topPendingMessage = this.appMessagesManager.getMessageByPeer(peerId, topPendingMid) as MyMessage; + const topMessage = this.appMessagesManager.getMessageByPeer(peerId, topMid) as MyMessage; + if(!topMid || (topPendingMessage && (!topMessage || topPendingMessage?.date > topMessage?.date))) { + dialog.top_message = topMid = topPendingMid; + this.appMessagesManager.getHistoryStorage(peerId).maxId = topPendingMid; } } @@ -811,7 +812,7 @@ export default class DialogsStorage extends AppManager { this.log.error('applyConversation lun', dialog, d); } */ - if(topMessage || dialog.draft?._ === 'draftMessage') { + if(topMid || dialog.draft?._ === 'draftMessage') { if(this.saveDialog(dialog)) { updatedDialogs.set(peerId, dialog); } diff --git a/src/scss/partials/_avatar.scss b/src/scss/partials/_avatar.scss index dbc0f82d9..4937f8f5c 100644 --- a/src/scss/partials/_avatar.scss +++ b/src/scss/partials/_avatar.scss @@ -78,6 +78,7 @@ avatar-element { } */ &.tgico-deletedaccount { + overflow: hidden; --color-top: var(--peer-avatar-archive-top); --color-bottom: var(--peer-avatar-archive-bottom); diff --git a/src/scss/partials/_badge.scss b/src/scss/partials/_badge.scss index 277481614..484dc2e76 100644 --- a/src/scss/partials/_badge.scss +++ b/src/scss/partials/_badge.scss @@ -5,11 +5,18 @@ */ .badge { + --size: 1.375rem; + --padding: .4375rem; border-radius: .75rem; font-weight: var(--font-weight-bold); color: var(--badge-text-color); font-size: .875rem; text-align: center; + + height: var(--size); + min-width: var(--size); + line-height: var(--size) !important; + padding: 0 var(--padding); @include animation-level(2) { transition: background-color .2s ease-in-out; @@ -20,24 +27,18 @@ } &-20 { - height: 1.25rem; - min-width: 1.25rem; - line-height: 1.25rem !important; - padding: 0 .375rem; + --size: 1.25rem; + --padding: .375rem; } &-24 { - height: 1.5rem; - min-width: 1.5rem; - line-height: 1.5rem !important; - padding: 0 .5rem; + --size: 1.5rem; + --padding: .5rem; + } - &.tgico { - // width: 1.5rem; - - &:before { - font-size: 1.5rem; - } + &.tgico { + &:before { + font-size: var(--size); } } diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index 92bdc2de3..bac285e92 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -675,7 +675,7 @@ $btn-menu-z-index: 4; display: flex; align-items: center; padding: 0 1rem; - height: 3.5rem; + height: 3rem; //width: auto; //text-transform: capitalize; font-weight: normal; @@ -690,7 +690,7 @@ $btn-menu-z-index: 4; &.danger { @include hover-background-effect(red); } - + &.primary { @include hover-background-effect(primary); } @@ -700,6 +700,10 @@ $btn-menu-z-index: 4; color: var(--secondary-text-color); font-size: 1.5rem; margin-right: 2rem; + + @include respond-to(handhelds) { + margin-right: 1.5rem; + } } &.btn-short:before { diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index 0212d48d8..4448271a0 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -1680,6 +1680,11 @@ $chat-input-border-radius: 1rem; padding: 0 $chat-padding-handhelds; } + &:after { + content: " "; + height: .125rem; + } + &.is-chat { .is-in { //margin-left: 45px; diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 641d0169e..077925223 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -85,6 +85,7 @@ $bubble-border-radius-big: 12px; } .bubble { + --line-height: var(--messages-line-height); position: relative; z-index: 1; margin: 0 auto $bubble-margin; diff --git a/src/scss/partials/_chatTopbar.scss b/src/scss/partials/_chatTopbar.scss index 22b5e5818..9868c261b 100644 --- a/src/scss/partials/_chatTopbar.scss +++ b/src/scss/partials/_chatTopbar.scss @@ -180,6 +180,7 @@ body.is-right-column-shown { max-width: calc(100% - 1.5rem); display: flex; align-items: center; + font-weight: var(--font-weight-bold); span.emoji { vertical-align: inherit; diff --git a/src/scss/partials/_chatlist.scss b/src/scss/partials/_chatlist.scss index 3f172f27b..d78a289f7 100644 --- a/src/scss/partials/_chatlist.scss +++ b/src/scss/partials/_chatlist.scss @@ -129,7 +129,7 @@ .search-super { .search-group { margin-bottom: 0px; - padding: 4px 0 0; + padding: 0 0 .5rem; &__name { padding-top: 1rem; @@ -138,7 +138,6 @@ } } } - } ul.chatlist { @@ -151,8 +150,6 @@ ul.chatlist { } .chatlist { - //--avatarSize: 54px; - //--height: 72px; margin: 0; display: flex; flex-direction: column; @@ -164,40 +161,11 @@ ul.chatlist { -webkit-user-select: none; /* disable selection/Copy of UIWebView */ -webkit-touch-callout: none; /* disable the IOS popup when long-press on a link */ - /* &.chatlist-avatar-48 { - --avatarSize: 48px; - } - - @include respond-to(handhelds) { - &.chatlist-handhelds-66 { - --height: 66px; - } - } */ - &-chat { --background: unset; - //height: var(--height); - height: 72px; - //max-height: var(--height); - border-radius: $border-radius-medium; - display: flex; - align-items: flex-start; // TODO: проверить разницу в производительности с align-items: center; - flex-direction: row; - position: relative; - cursor: pointer; - padding: .5625rem; - /* padding-top: calc((var(--height) - var(--avatarSize)) / 2); - padding-bottom: calc((var(--height) - var(--avatarSize)) / 2); - padding-right: 8.5px; - padding-left: 8.5px; */ - overflow: hidden; - background: var(--background); + background: var(--background) !important; -webkit-user-drag: none; - @include respond-to(handhelds) { - border-radius: 0; - } - //@include hover-background-effect(); @include hover(gray, --background, false); @@ -227,31 +195,14 @@ ul.chatlist { } } - p { - margin: 0; - display: flex; - flex-direction: row; - align-items: flex-start; - height: 27px; - } - - a { - color: inherit; + i { + font-style: normal; } .text-highlight { color: var(--primary-text-color); } - /* img.emoji { - margin-right: .25rem; - margin-left: .25rem; - - &:first-child { - margin-left: 0; - } - } */ - &.menu-open { --background: var(--light-secondary-text-color); } @@ -261,7 +212,8 @@ ul.chatlist { --background: var(--primary-color) !important; //background: var(--light-secondary-text-color); - .user-caption, + .row-subtitle, + .row-title, .tgico-chatspinned:before, //.user-title:after, .user-title, @@ -270,7 +222,7 @@ ul.chatlist { .premium-icon, .verified-icon, .sending-status-icon { - color: #fff; + color: #fff !important; } .badge-fake { @@ -278,7 +230,8 @@ ul.chatlist { border-color: #fff; } - b { + .primary-text, + .danger { color: #fff !important; } @@ -316,17 +269,6 @@ ul.chatlist { } } - /* .user-title, - .dialog-title-details, - .user-last-message */&-chat span { - //display: inline-block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - //margin: .1rem 0; - line-height: 27px; - } - .peer-typing-container { --color: var(--secondary-text-color); @@ -347,12 +289,8 @@ ul.chatlist { } &-subtitle { - margin-top: -3px; - &-badge { display: block !important; - margin-top: 4px; - margin-right: -3px; margin-left: .5rem; flex: 0 0 auto; @@ -368,13 +306,21 @@ ul.chatlist { &-media { width: 1.25rem; height: 1.25rem; + line-height: 1.25rem; position: relative; flex: 0 0 auto; border-radius: .25rem; - margin-top: -0.125rem; margin-right: 0.375rem; display: inline-block; - vertical-align: middle; + + &:before { + content: " "; + display: inline-block; + width: inherit; + height: inherit; + min-width: inherit; + min-height: inherit; + } &.is-round { border-radius: 50%; @@ -400,6 +346,8 @@ ul.chatlist { height: inherit; object-fit: cover; border-radius: inherit; + top: auto; + bottom: auto; } } } @@ -431,15 +379,8 @@ ul.chatlist { flex: 0 0 auto; } - .user-caption { - overflow: hidden; - color: var(--secondary-text-color); - flex: 1 1 auto; - padding: .0625rem .4375rem .0625rem .5625rem; - } - - .dialog-avatar, - .user-caption { + .dialog-avatar, + .row-row { pointer-events: none; position: relative; // for z-index } @@ -454,12 +395,6 @@ ul.chatlist { width: 18px; height: 18px; } - - /* span.emoji { - &:first-of-type:not(:first-child) { - margin-left: .125rem; - } - } */ } .user-last-message { @@ -481,11 +416,6 @@ ul.chatlist { .user-last-message { flex-grow: 1; position: relative; // * for custom emoji - - i { - font-style: normal; - //color: var(--primary-color); - } } .message-status { @@ -505,10 +435,6 @@ ul.chatlist { } } - /* .message-time { - vertical-align: middle; - } */ - .tgico-chatspinned { background: transparent; @@ -530,8 +456,7 @@ ul.chatlist { } } - .tgico-chatspinned/* , - .tgico-mention */ { + .tgico-chatspinned { position: relative; &:before { @@ -546,12 +471,12 @@ ul.chatlist { background-color: var(--chatlist-status-color) !important; html.is-mac & { - line-height: 22px !important; + line-height: 1.25rem !important; } } .mention-badge { - margin-right: -2px; + margin-right: -.125rem; } /* .tgico-mention { @@ -619,87 +544,4 @@ ul.chatlist { // margin-top: -.5rem; } } - - // * supernew correct layout - &-new { - .chatlist-chat { - height: 4.5rem; - padding: 0 .75rem; - align-items: center; - } - - .user-caption { - padding-left: .75rem; - } - - p { - height: auto !important; - } - - span { - line-height: var(--line-height) !important; - } - - .dialog-subtitle { - margin-top: .125rem; - } - - .user-last-message { - font-size: .875rem; - } - } -} - -// use together like class="chatlist-container contacts-container" -.contacts-container, -.search-group-contacts { - .chatlist-chat { - padding: .75rem; - - @include respond-to(handhelds) { - height: 66px; - padding-top: 9px; - padding-bottom: 9px; - } - } - - .user-caption { - padding: 1px 3.5px 1px 13px; - - @include respond-to(handhelds) { - padding: 0 4px 0 14px; - } - } - - .user-title, - b, - .user-last-message b { - font-weight: normal; - } - - p { - height: 24px; - - @include respond-to(handhelds) { - height: 26px; - } - } - - span.user-last-message { - font-size: 14px; - } -} - -.chatlist-new.chatlist-48 { - .chatlist-chat { - height: 3.5rem; - } - - .user-caption { - padding-left: 1.125rem; - } - - .dialog-subtitle { - margin-top: .0625rem; - } } diff --git a/src/scss/partials/_input.scss b/src/scss/partials/_input.scss index f50e464d7..73d618038 100644 --- a/src/scss/partials/_input.scss +++ b/src/scss/partials/_input.scss @@ -95,6 +95,7 @@ } &-input { + // --padding: calc((var(--height) - var(--border-width) * 2 - var(--line-height)) / 2); --padding: 1rem; --padding-horizontal: 1rem; --border-width: 1px; diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index d6539013a..31ed69936 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -345,40 +345,52 @@ .chatlist { display: flex; flex-direction: row; - padding-left: 4px; margin-top: -1px; - padding-bottom: 1px; - } + padding: 0 0 1px; - .chatlist-chat { - height: 98px; - border-radius: 10px; - max-width: 78px; - width: 78px; - align-items: center; - display: flex; - flex-direction: column; - padding: 12px 0 0 !important; - margin: 0 5px 0 0; - flex: 0 0 auto; + &:before, + &:after { + content: " "; + display: inline-block; + width: .3125rem; + flex: 0 0 auto; + } - @include respond-to(handhelds) { - width: 77px; - max-width: 77px; + &-chat { + width: 4.875rem; + max-width: 4.875rem; + height: 6.125rem; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: center; + flex: 0 0 auto; + padding: 0 !important; } } - .dialog-title-details, .dialog-subtitle { - display: none; - } + .dialog { + &-title-details, + &-subtitle { + display: none; + } + + &-title { + max-width: 65px; + padding-bottom: .75rem; - .user-caption { - max-width: 65px; - padding: 2px 0px 9px; - font-size: 12px; + .user-title { + font-size: .75rem !important; + } - @include respond-to(handhelds) { - max-width: 56px; + .peer-title { + font-weight: var(--font-weight-normal) !important; + } + } + + &-avatar { + left: auto !important; + top: .75rem !important; } } @@ -535,7 +547,6 @@ @include respond-to(handhelds) { width: 100%; - padding: 0 .25rem; } } } @@ -550,7 +561,9 @@ } } -.new-channel-container, .new-group-container, .edit-profile-container { +.new-channel-container, +.new-group-container, +.edit-profile-container { .sidebar-content { flex-direction: column; } @@ -560,7 +573,7 @@ } .caption { - font-size: 0.875rem; + font-size: .875rem; margin-top: 14px; margin-left: 23px; color: var(--secondary-text-color); @@ -642,8 +655,8 @@ .caption { text-align: center; color: var(--secondary-text-color); - font-size: 14px; - line-height: var(--line-height); + font-size: var(--font-size-14); + line-height: var(--line-height-14); max-width: 20rem; margin: 0 auto; } @@ -697,21 +710,6 @@ } } -.folder-list { - .chatlist-chat { - padding: 9px 12px; - height: 50px; - } - - .user-caption { - padding: 3px 28px 6px 27px; - } - - p span { - font-weight: normal; - } -} - .folder-categories { .checkbox-field { position: absolute; @@ -719,31 +717,6 @@ } } -.folder-category-button { - height: 50px; -} - -.popup-forward, .included-chatlist-container { - .selector { - .chatlist { - &-chat { - padding: 7px .75rem !important; - height: 3.75rem; - } - - .user-caption { - padding: 0px 0px 0 14px; - margin-top: -2px; - } - - .user-last-message { - font-size: 15px; - margin-top: 2px; - } - } - } -} - .included-chatlist-container { .sidebar-left-section { padding-bottom: 0; @@ -786,27 +759,6 @@ font-size: 22px; //} } - - @include respond-to(handhelds) { - .chatlist-chat { - height: 62px; - padding-top: 7px; - padding-bottom: 7px; - } - - .user-caption { - margin-top: -2px; - } - - .user-title { - font-weight: var(--font-weight-bold) !important; - } - - .dialog-avatar { - --size: 46px; - --multiplier: 1.173913; - } - } } @include respond-to(handhelds) { @@ -913,7 +865,8 @@ } } - .checkbox-field, .radio-field { + .checkbox-field, + .radio-field { margin: 0; } @@ -1081,9 +1034,9 @@ .active-sessions-container { .row { - margin-top: 0; - padding-top: 1rem; - padding-bottom: .9375rem; + // margin-top: 0; + // padding-top: 1rem; + // padding-bottom: .9375rem; &-title:first-child { font-weight: var(--font-weight-bold); @@ -1113,21 +1066,6 @@ } .blocked-users-container { - .chatlist-chat { - height: 66px; - padding-top: 9px; - padding-bottom: 9px; - } - - .user-caption { - padding-left: .75rem; - } - - .dialog-subtitle { - margin-top: -.375rem; - font-size: .875rem; - } - ul { @include respond-to(not-handhelds) { padding: 0 .6875rem; diff --git a/src/scss/partials/_poll.scss b/src/scss/partials/_poll.scss index 767b18688..070f926a0 100644 --- a/src/scss/partials/_poll.scss +++ b/src/scss/partials/_poll.scss @@ -79,6 +79,7 @@ poll-element { &-avatars { display: flex; margin-left: 18px; + cursor: pointer; } &-avatar { diff --git a/src/scss/partials/_profile.scss b/src/scss/partials/_profile.scss index ba601cf06..829cb9f99 100644 --- a/src/scss/partials/_profile.scss +++ b/src/scss/partials/_profile.scss @@ -125,10 +125,6 @@ } } - .profile-name { - margin-bottom: -1px; - } - .profile-subtitle, .verified-icon, .premium-icon { @@ -299,8 +295,8 @@ &-name { text-align: center; - font-size: 1.25rem; - line-height: var(--line-height); + font-size: var(--font-size-20); + line-height: var(--line-height-20); font-weight: var(--font-weight-bold); overflow: hidden; max-width: 21.25rem; diff --git a/src/scss/partials/_rightSidebar.scss b/src/scss/partials/_rightSidebar.scss index ff3c7a89f..efb1c8926 100644 --- a/src/scss/partials/_rightSidebar.scss +++ b/src/scss/partials/_rightSidebar.scss @@ -564,7 +564,8 @@ } } - &-content-music, &-content-voice { + &-content-music, + &-content-voice { .search-super-month-items { padding: 20px 15px 0px 20px; @@ -596,19 +597,6 @@ .chatlist { padding-top: .5rem; padding-bottom: .5rem; - - .chatlist-chat { - padding: .75rem; - } - - .user-caption { - padding-left: .75rem; - } - - .dialog-subtitle { - font-size: .875rem; - margin-top: -.375rem; - } } } @@ -692,9 +680,9 @@ } } } - + .search-group.is-short { - li:nth-child(n + 4) { + .chatlist-chat:nth-child(n + 4) { display: none; } } @@ -794,22 +782,12 @@ } &-more { - padding-top: 13px; - padding-bottom: 13px; - cursor: pointer; - user-select: none; - position: relative; + margin: 0 .5rem; + width: auto; - @include respond-to(not-handhelds) { - padding-left: 8px; - } - - .tgico-down { - float: left; - padding-right: 32px; - padding-left: 16.5px; - font-size: 24px; - color: var(--secondary-text-color); + @include respond-to(handhelds) { + margin: 0; + border-radius: 0; } } @@ -827,30 +805,8 @@ hr { margin-bottom: 15px; margin-top: 7px; + display: block !important; } - - .user-caption { - padding: 3px 28px 6px; - } - - .user-title { - font-weight: normal; - } - - .chatlist-chat { - height: 50px; - padding: 9px; - - @include respond-to(not-handhelds) { - padding: 9px 12px; - } - } - } -} - -#search-gifs-container { - .gifs-masonry { - margin-top: -2.5px; } } diff --git a/src/scss/partials/_row.scss b/src/scss/partials/_row.scss index f90b148a7..96c3c44d9 100644 --- a/src/scss/partials/_row.scss +++ b/src/scss/partials/_row.scss @@ -16,7 +16,8 @@ $row-border-radius: $border-radius-medium; flex-direction: column; justify-content: center; - &.no-subtitle { + &.no-subtitle, + &.row-small { min-height: 3rem; padding-top: .1875rem; padding-bottom: .1875rem; @@ -31,25 +32,43 @@ $row-border-radius: $border-radius-medium; opacity: var(--disabled-opacity); } + &.no-wrap { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + a { position: relative; z-index: 1; } - &-title-row { + &-title-row, + & > &-title { + order: 0; + } + + &-big { + min-height: 4.5rem; + padding: .5625rem .75rem .5625rem 1rem; + } + + &-row { display: flex; justify-content: space-between; align-items: center; - order: 0; - .row-title { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + .row-title, + .row-subtitle { + @include text-overflow(true); flex: 1 1 auto; } } + &-subtitle-row, + & > &-subtitle { + order: 1; + } + // &-title, // &-title-row, // &-subtitle { @@ -58,41 +77,51 @@ $row-border-radius: $border-radius-medium; &-title { color: var(--primary-text-color); + font-size: var(--font-size); line-height: var(--line-height); - order: 0; @include text-overflow(false); &-right { - flex: 0 0 auto !important; - margin-left: 1rem; - &-secondary { color: var(--secondary-text-color); } } } + &-title-right, + &-subtitle-right { + flex: 0 0 auto !important; + margin-left: 1rem; + } + &-midtitle { - font-size: .875rem; + font-size: var(--font-size-14); order: 1; } &-with-padding { padding-left: 4.5rem; + @include respond-to(handhelds) { + padding-left: 4rem; + } + .row-title.tgico:before { position: absolute; left: 1rem; font-size: 1.5rem; color: var(--secondary-text-color); pointer-events: none; - margin-top: -.125rem; + + // margin-top: -.125rem; + top: 50%; + transform: translateY(-50%); } - .row-subtitle:not(:empty):not(.hide) + .row-title.tgico:before { - margin-top: .25rem; - } + // .row-subtitle:not(:empty):not(.hide) + .row-title.tgico:before { + // margin-top: .25rem; + // } } &-clickable { @@ -144,11 +173,9 @@ $row-border-radius: $border-radius-medium; &-subtitle { color: var(--secondary-text-color) !important; - font-size: .875rem !important; + font-size: var(--font-size-14) !important; line-height: var(--line-height-14); - margin-top: .125rem; - margin-bottom: .0625rem; - order: 1; + margin-top: .1875rem; &:empty { display: none; @@ -172,11 +199,23 @@ $row-border-radius: $border-radius-medium; left: .75rem !important; } + &-abitbigger { + width: 2.625rem !important; + height: 2.625rem !important; + left: .75rem !important; + } + &-big { width: 3rem !important; height: 3rem !important; left: .5rem !important; } + + &-bigger { + width: 3.375rem !important; + height: 3.375rem !important; + left: .5625rem !important; // it's wrong but old chatlist has it + } } &.menu-open { diff --git a/src/scss/partials/_selector.scss b/src/scss/partials/_selector.scss index ed51b4452..6d56f9d41 100644 --- a/src/scss/partials/_selector.scss +++ b/src/scss/partials/_selector.scss @@ -143,32 +143,6 @@ } */ } - .chatlist { - &-chat { - padding-top: .75rem; - padding-bottom: .75rem; - - @include respond-to(handhelds) { - height: 66px; - padding-top: 9px; - padding-bottom: 9px; - } - } - - .user-caption { - padding-left: .75rem; - padding-right: 0; - } - - p { - height: 24px !important; - } - - span.user-last-message { - font-size: 14px; - } - } - > hr { margin: 0; padding: 0; @@ -180,8 +154,11 @@ } .checkbox-field { - margin: 0; - padding: 0; + --offset-left: 0 !important; + pointer-events: none; + position: absolute !important; + margin: 0 !important; + padding: 0 !important; transform: translateY(-50%); top: 50%; z-index: 1; @@ -193,8 +170,6 @@ } .checkbox-field-round { - pointer-events: none; - position: absolute; right: 1.125rem; --size: 1.25rem; @@ -206,4 +181,20 @@ --offset: 6px; } } + + &-square { + $add: 3rem; + + .checkbox-field { + left: 1.25rem !important; + } + + .chatlist-chat { + padding-left: #{4.5rem + $add} !important; // 4.5 + x + } + + .dialog-avatar { + margin-left: #{$add} !important; + } + } } diff --git a/src/scss/partials/popups/_forward.scss b/src/scss/partials/popups/_forward.scss index 1d21f26dc..cedb23c6f 100644 --- a/src/scss/partials/popups/_forward.scss +++ b/src/scss/partials/popups/_forward.scss @@ -19,21 +19,23 @@ &-header { flex: 0 0 auto; - margin-bottom: 3px; + margin-bottom: 7px; padding: 0 1rem; @include respond-to(handhelds) { - padding-left: .9375rem; + padding-left: .8125rem; } } &-title { flex-grow: 1; padding: 0; + height: 100%; } } - .selector, .chatlist-container { + .selector, + .chatlist-container { height: auto; overflow: hidden; display: flex; @@ -44,24 +46,19 @@ .selector { &-search-input { - font-size: 1.25rem; + font-size: var(--font-size-20); + line-height: 1; padding: .5rem 1.5rem; width: 100%; - line-height: var(--line-height); + height: 100%; @include respond-to(handhelds) { - padding-left: 1.0625rem; + padding-left: 1.1875rem; } } .chatlist { margin-top: 0 !important; - - &-chat { - height: 3.875rem !important; - padding-top: .5rem !important; - padding-bottom: .5rem !important; - } } } diff --git a/src/scss/partials/popups/_groupCall.scss b/src/scss/partials/popups/_groupCall.scss index f6c6b66ae..476ef69dd 100644 --- a/src/scss/partials/popups/_groupCall.scss +++ b/src/scss/partials/popups/_groupCall.scss @@ -154,7 +154,6 @@ } &-participant { - align-items: center; // border-radius: 0; padding-right: .5rem; @@ -175,8 +174,14 @@ } } + .dialog-title, + .dialog-subtitle { + padding-right: 2.5rem; + } + &-muted-icon-container { - flex: 0 0 auto; + position: absolute !important; + right: .5rem; } &-video { @@ -382,10 +387,6 @@ color: var(--gc-secondary-text-color); } - .dialog-subtitle { - margin-top: -.25rem; - } - // .user-caption { // padding-right: 1rem; // } diff --git a/src/scss/style.scss b/src/scss/style.scss index cdf32b733..af0bffc74 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -86,6 +86,7 @@ $chat-input-inner-padding-handhelds: .25rem; --message-highlightning-color: hsla(85.5319, 36.9171%, 40.402%, .4);//rgba(77, 142, 80, .4); --messages-container-width: #{$messages-container-width}; --messages-text-size: 16px; + --messages-line-height: 1.3125; --messages-secondary-text-size: calc(var(--messages-text-size) - 2px); --messages-secondary-line-height: calc(var(--messages-secondary-text-size) + 4px); --messages-time-text-size: calc(var(--messages-text-size) - 4px); @@ -93,15 +94,16 @@ $chat-input-inner-padding-handhelds: .25rem; --messages-custom-emoji-size: calc(var(--messages-text-size) + 4px); --bubble-transition-in: transform var(--transition-standard-in), opacity var(--transition-standard-in); --bubble-transition-out: transform var(--transition-standard-out), opacity var(--transition-standard-out); - --line-height: 1.3125; - --line-height-20: 23px; + --line-height-20: 26px; --line-height-16: 21px; --line-height-14: 18px; --line-height-12: 16px; + --line-height: 1.3125; --font-size-20: 20px; --font-size-16: 16px; --font-size-14: 14px; --font-size-12: 12px; + --font-size: var(--font-size-16); --esg-sticker-size: 72px; --esg-custom-emoji-size: 36px; --popup-sticker-size: 80px; @@ -111,6 +113,7 @@ $chat-input-inner-padding-handhelds: .25rem; --menu-backdrop-filter: blur(50px); --font-monospace: 'Roboto Mono', monospace; --font-weight-bold: 500; + --font-weight-normal: 400; --selection-background-color: rgba(var(--primary-color-rgb), .4); --selection-color: inherit; @@ -418,14 +421,15 @@ $chat-input-inner-padding-handhelds: .25rem; @import "partials/pages/chats"; @import "partials/pages/password"; -html, body { +html, +body { height: 100%; width: 100%; margin: 0; padding: 0; + overflow: hidden; // + disable overscroll behavior on macOS @include respond-to(handhelds) { - overflow: hidden; height: calc(var(--vh, 1vh) * 100); } @@ -619,7 +623,8 @@ input:-webkit-autofill:active { } } -.blue, .primary { +.blue, +.primary { color: var(--primary-color) !important; .c-ripple__circle { @@ -627,13 +632,19 @@ input:-webkit-autofill:active { } } +.primary-text { + color: var(--primary-text-color) !important; +} + .color-premium { background: var(--premium-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } -.blue:before, .primary:before, .danger:before { +.blue:before, +.primary:before, +.danger:before { color: inherit !important; } @@ -771,16 +782,9 @@ hr { padding-bottom: .5rem; } -.user-title, b/* , .user-last-message b */ { +b { color: var(--primary-text-color); - // font-weight: bolder; font-weight: var(--font-weight-bold); - //font-weight: normal; -} - -.user-last-message b { - font-weight: 400; - //margin-right: .25rem; } .avatar-edit { @@ -1576,13 +1580,18 @@ middle-ellipsis-element { &-caption { margin: -0.1875rem 0 1rem; - font-size: .875rem; + font-size: var(--font-size-14); + line-height: var(--line-height-14); padding: 0 1.5rem; &:first-child { margin-top: .8125rem; margin-bottom: .8125rem; } + + @include respond-to(handhelds) { + padding: 0 1rem; + } } &-container { @@ -1997,6 +2006,67 @@ hr { } } +.chatlist-chat { + padding-left: 4.5rem !important; + + span { + @include text-overflow(true); + } + + .row { + &-title { + font-size: var(--font-size-16) !important; + line-height: 1.375rem; + + &-right-secondary { + margin-top: -.4375rem; + font-size: var(--font-size-12) !important; + line-height: var(--line-height-12) !important; + } + + // .peer-title { + // font-weight: var(--font-weight-bold); + // } + } + + &-row { + height: 1.375rem; + } + } + + &.chatlist-chat-small { + + } + + &.chatlist-chat-abitbigger { + .row { + &-subtitle { + margin-top: 0; + } + } + } + + &.chatlist-chat-bigger { + .row { + &-subtitle { + margin-top: 0; + font-size: var(--font-size-16) !important; + line-height: 1.375rem; + + &-row { + margin-top: .125rem; + } + } + + &-title { + .peer-title { + font-weight: var(--font-weight-bold); + } + } + } + } +} + // .contacts-container { // .chatlist-chat { // /* align-items: center; */