diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index 0ddfa602..e1e0b3ee 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -32,6 +32,8 @@ import { isSafari } from "../helpers/userAgent"; import { LangPackKey, i18n } from "../lib/langPack"; import findUpClassName from "../helpers/dom/findUpClassName"; import { getMiddleware } from "../helpers/middleware"; +import appProfileManager from "../lib/appManagers/appProfileManager"; +import { ChannelParticipant, ChatFull, ChatParticipant, ChatParticipants } from "../layer"; //const testScroll = false; @@ -824,6 +826,81 @@ export default class AppSearchSuper { } else return Promise.resolve(); } + private loadMembers(mediaTab: SearchSuperMediaTab) { + const id = -this.searchContext.peerId; + const middleware = this.middleware.get(); + let promise: Promise; + + const renderParticipants = async(participants: (ChatParticipant | ChannelParticipant)[]) => { + if(this.loadMutex) { + await this.loadMutex; + + if(!middleware()) { + return; + } + } + + let list = mediaTab.contentTab.firstElementChild as HTMLUListElement; + if(!list) { + list = appDialogsManager.createChatList(); + appDialogsManager.setListClickListener(list, undefined, undefined, true, true); + mediaTab.contentTab.append(list); + this.afterPerforming(1, mediaTab.contentTab); + } + + participants.forEach(participant => { + let {dialog, dom} = appDialogsManager.addDialogNew({ + dialog: participant.user_id, + container: list, + drawStatus: false, + avatarSize: 48, + autonomous: true, + meAsSaved: false, + rippleEnabled: false + }); + + let status = appUsersManager.getUserStatusString(participant.user_id); + dom.lastMessageSpan.append(status); + }); + }; + + if(appChatsManager.isChannel(id)) { + const LOAD_COUNT = 50; + promise = appProfileManager.getChannelParticipants(id, undefined, LOAD_COUNT, this.nextRates[mediaTab.inputFilter]).then(participants => { + if(!middleware()) { + return; + } + + let list = mediaTab.contentTab.firstElementChild as HTMLUListElement; + this.nextRates[mediaTab.inputFilter] = (list ? list.childElementCount : 0) + participants.participants.length; + + if(participants.participants.length < LOAD_COUNT) { + this.loaded[mediaTab.inputFilter] = true; + } + + return renderParticipants(participants.participants); + }); + } else { + promise = (appProfileManager.getChatFull(id) as Promise).then(chatFull => { + if(!middleware()) { + return; + } + + console.log('anymore', chatFull); + this.loaded[mediaTab.inputFilter] = true; + return renderParticipants((chatFull.participants as ChatParticipants.chatParticipants).participants); + }); + } + + return this.loadPromises[mediaTab.inputFilter] = promise.finally(() => { + if(!middleware()) { + return; + } + + this.loadPromises[mediaTab.inputFilter] = null; + }); + } + private loadType(mediaTab: SearchSuperMediaTab, justLoad: boolean, loadCount: number, middleware: () => boolean) { const type = mediaTab.inputFilter; @@ -831,6 +908,10 @@ export default class AppSearchSuper { return this.loadPromises[type]; } + if(mediaTab.type === 'members') { + return this.loadMembers(mediaTab); + } + const history = this.historyStorage[type] ?? (this.historyStorage[type] = []); if(type === 'inputMessagesFilterEmpty' && !history.length) { @@ -1023,6 +1104,10 @@ export default class AppSearchSuper { return containers[dateTimestamp]; } + public canViewMembers() { + return this.searchContext.peerId < 0 && !appChatsManager.isBroadcast(-this.searchContext.peerId) && appChatsManager.hasRights(-this.searchContext.peerId, 'view_participants'); + } + public cleanup() { this.loadPromises = {}; this.loaded = {}; @@ -1035,6 +1120,13 @@ export default class AppSearchSuper { this.usedFromHistory[mediaTab.inputFilter] = -1; }); + // * must go to first tab (это костыль) + const membersTab = this.mediaTabsMap.get('members'); + if(membersTab) { + const tab = this.canViewMembers() ? membersTab : this.mediaTabs[this.mediaTabs.indexOf(membersTab) + 1]; + this.mediaTab = tab; + } + this.middleware.clean(); this.cleanScrollPositions(); } @@ -1045,7 +1137,7 @@ export default class AppSearchSuper { }); } - public cleanupHTML() { + public cleanupHTML(goFirst = false) { if(this.urlsToRevoke.length) { this.urlsToRevoke.forEach(url => { URL.revokeObjectURL(url); @@ -1056,6 +1148,10 @@ export default class AppSearchSuper { this.mediaTabs.forEach((tab) => { tab.contentTab.innerHTML = ''; + /* if(this.hideEmptyTabs) { + tab.menuTab.classList.add('hide'); + } */ + if(tab.type === 'chats') { return; } @@ -1075,6 +1171,18 @@ export default class AppSearchSuper { } }); + if(goFirst) { + const membersTab = this.mediaTabsMap.get('members'); + if(membersTab) { + let idx = this.canViewMembers() ? 0 : 1; + membersTab.menuTab.classList.toggle('hide', idx !== 0); + + this.selectTab(idx, false); + } else { + this.selectTab(0, false); + } + } + this.monthContainers = {}; this.searchGroupMedia.clear(); this.scrollable.scrollTop = 0; diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index d20c3f0f..07d00a31 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -725,8 +725,30 @@ export default class ChatBubbles { return; } + const nameDiv = findUpClassName(target, 'peer-title') || findUpClassName(target, 'name') || findUpTag(target, 'AVATAR-ELEMENT'); + if(nameDiv) { + target = nameDiv || target; + const peerId = +(target.dataset.peerId || target.getAttribute('peer')); + const savedFrom = target.dataset.savedFrom; + if(savedFrom) { + const splitted = savedFrom.split('_'); + const peerId = +splitted[0]; + const msgId = +splitted[1]; + + this.chat.appImManager.setInnerPeer(peerId, msgId); + } else { + if(peerId) { + this.chat.appImManager.setInnerPeer(peerId); + } else { + toast(I18n.format('HidAccount', true)); + } + } + + return; + } + //this.log('chatInner click:', target); - const isVideoComponentElement = target.tagName === 'SPAN' && !target.classList.contains('peer-title'); + const isVideoComponentElement = target.tagName === 'SPAN'; /* if(isVideoComponentElement) { const video = target.parentElement.querySelector('video') as HTMLElement; if(video) { @@ -748,7 +770,7 @@ export default class ChatBubbles { return; } - if((target.tagName === 'IMG' && !target.classList.contains('emoji') && target.parentElement.tagName !== "AVATAR-ELEMENT" && !target.classList.contains('document-thumb')) + if((target.tagName === 'IMG' && !target.classList.contains('emoji') && !target.classList.contains('document-thumb')) || target.classList.contains('album-item') || isVideoComponentElement || (target.tagName === 'VIDEO' && !bubble.classList.contains('round'))) { @@ -818,9 +840,9 @@ export default class ChatBubbles { return; } - if(['IMG', 'DIV', "AVATAR-ELEMENT", 'SPAN'/* , 'A' */].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV'); + if(['IMG', 'DIV', 'SPAN'/* , 'A' */].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV'); - if(['DIV', 'SPAN', 'AVATAR-ELEMENT'].indexOf(target.tagName) !== -1/* || target.tagName === 'A' */) { + if(['DIV', 'SPAN'].indexOf(target.tagName) !== -1/* || target.tagName === 'A' */) { if(target.classList.contains('goto-original')) { const savedFrom = bubble.dataset.savedFrom; const splitted = savedFrom.split('_'); @@ -833,35 +855,6 @@ export default class ChatBubbles { const mid = +bubble.dataset.mid; new PopupForward(this.peerId, [mid]); //appSidebarRight.forwardTab.open([mid]); - return; - } else if(target.classList.contains('peer-title') || target.classList.contains('name')) { - target = findUpClassName(target, 'name') || target; - const peerId = +target.dataset.peerId; - const savedFrom = target.dataset.savedFrom; - if(savedFrom) { - const splitted = savedFrom.split('_'); - const peerId = +splitted[0]; - const msgId = +splitted[1]; - - this.chat.appImManager.setInnerPeer(peerId, msgId); - } else { - if(peerId) { - this.chat.appImManager.setInnerPeer(peerId); - } else { - toast(I18n.format('HidAccount', true)); - } - } - - return; - } else if(target.tagName === "AVATAR-ELEMENT") { - const peerId = +target.getAttribute('peer'); - - if(peerId) { - this.chat.appImManager.setInnerPeer(peerId); - } else { - toast(I18n.format('HidAccount', true)); - } - return; } @@ -889,14 +882,6 @@ export default class ChatBubbles { } */ //this.chat.setMessageId(, originalMessageId); } - } else if(target.tagName === 'IMG' && target.parentElement.tagName === "AVATAR-ELEMENT") { - let peerId = +target.parentElement.getAttribute('peer'); - - if(peerId) { - this.chat.appImManager.setInnerPeer(peerId); - } else { - toast(I18n.format('HidAccount', true)); - } } //console.log('chatInner click', e); diff --git a/src/components/sidebarRight/tabs/sharedMedia.ts b/src/components/sidebarRight/tabs/sharedMedia.ts index ca9a6d3e..41194010 100644 --- a/src/components/sidebarRight/tabs/sharedMedia.ts +++ b/src/components/sidebarRight/tabs/sharedMedia.ts @@ -39,6 +39,7 @@ import { forEachReverse } from "../../../helpers/array"; import appPhotosManager from "../../../lib/appManagers/appPhotosManager"; import renderImageFromUrl from "../../../helpers/dom/renderImageFromUrl"; import SwipeHandler from "../../swipeHandler"; +import { MOUNT_CLASS_TO } from "../../../config/debug"; let setText = (text: string, row: Row) => { fastRaf(() => { @@ -628,30 +629,29 @@ class PeerProfile { // TODO: отредактированное сообщение не изменится export default class AppSharedMediaTab extends SliderSuperTab { - public editBtn: HTMLElement; + private editBtn: HTMLElement; private peerId = 0; private threadId = 0; - public historiesStorage: { + private historiesStorage: { [peerId: number]: Partial<{ [type in SearchSuperType]: {mid: number, peerId: number}[] }> } = {}; - private log = logger('SM'/* , LogLevels.error */); - private cleaned: boolean; private searchSuper: AppSearchSuper; - public profile: PeerProfile; + private profile: PeerProfile; constructor(slider: SidebarSlider) { super(slider, false); } - protected init() { - this.container.id = 'shared-media-container'; - this.container.classList.add('profile-container'); + public init() { + const perf = performance.now(); + + this.container.classList.add('shared-media-container', 'profile-container'); // * header const newCloseBtn = Button('btn-icon sidebar-close-button', {noRipple: true}); @@ -769,6 +769,13 @@ export default class AppSharedMediaTab extends SliderSuperTab { }], scrollable: this.scrollable }); + + this.profile.element.append(this.searchSuper.container); + + const btnAddMembers = Button('btn-corner btn-circle', {icon: 'adduser'}); + this.content.append(btnAddMembers); + + console.log('construct shared media time:', performance.now() - perf); } public renderNewMessages(peerId: number, mids: number[]) { @@ -832,15 +839,16 @@ export default class AppSharedMediaTab extends SliderSuperTab { } public cleanupHTML() { + const perf = performance.now(); this.profile.cleanupHTML(); this.editBtn.style.display = 'none'; - this.searchSuper.cleanupHTML(); - this.searchSuper.selectTab(0, false); - if(!this.searchSuper.container.parentElement) { - this.profile.element.append(this.searchSuper.container); - } + this.searchSuper.cleanupHTML(true); + + this.container.classList.toggle('can-add-members', this.searchSuper.canViewMembers() && appChatsManager.hasRights(-this.peerId, 'invite_users')); + + console.log('cleanupHTML shared media time:', performance.now() - perf); } public setLoadMutex(promise: Promise) { @@ -850,13 +858,14 @@ export default class AppSharedMediaTab extends SliderSuperTab { public setPeer(peerId: number, threadId = 0) { if(this.peerId === peerId && this.threadId === peerId) return; + this.peerId = peerId; + this.threadId = threadId; + if(this.init) { this.init(); this.init = null; } - this.peerId = peerId; - this.threadId = threadId; this.searchSuper.setQuery({ peerId, //threadId, @@ -864,7 +873,6 @@ export default class AppSharedMediaTab extends SliderSuperTab { }); this.profile.setPeer(peerId, threadId); - this.cleaned = true; } public fillProfileElements() { @@ -892,3 +900,5 @@ export default class AppSharedMediaTab extends SliderSuperTab { this.scrollable.onScroll(); } } + +MOUNT_CLASS_TO && (MOUNT_CLASS_TO.AppSharedMediaTab = AppSharedMediaTab); diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 6ea133db..dc22cbbe 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -27,7 +27,7 @@ import appUsersManager from "./appUsersManager"; export type Channel = Chat.channel; -export type ChatRights = keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags'] | 'change_type' | 'change_permissions' | 'delete_chat'; +export type ChatRights = keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags'] | 'change_type' | 'change_permissions' | 'delete_chat' | 'view_participants'; export type UserTyping = Partial<{userId: number, action: SendMessageAction, timeout: number}>; @@ -289,6 +289,10 @@ export class AppChatsManager { case 'change_permissions': { return rights._ === 'chatAdminRights' && myFlags['ban_users']; } + + case 'view_participants': { + return !!(chat._ === 'chat' || !chat.pFlags.broadcast || chat.pFlags.creator || chat.admin_rights); + } } return true; diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 218ec8c0..8e21dd00 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -920,9 +920,11 @@ export class AppDialogsManager { this.loadDialogs(side); }; - public setListClickListener(list: HTMLUListElement, onFound?: () => void, withContext = false, autonomous = false) { + public setListClickListener(list: HTMLUListElement, onFound?: () => void, withContext = false, autonomous = false, openInner = false) { let lastActiveListElement: HTMLElement; + const setPeerFunc = (openInner ? appImManager.setInnerPeer : appImManager.setPeer).bind(appImManager); + list.dataset.autonomous = '' + +autonomous; list.addEventListener('mousedown', (e) => { if(e.button !== 0) return; @@ -955,9 +957,9 @@ export class AppDialogsManager { const peerId = +elem.dataset.peerId; const lastMsgId = +elem.dataset.mid || undefined; - appImManager.setPeer(peerId, lastMsgId); + setPeerFunc(peerId, lastMsgId); } else { - appImManager.setPeer(0); + setPeerFunc(0); } }, {capture: true}); diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index 1419e1c5..7afae8d8 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -99,9 +99,7 @@ border-radius: $border-radius-medium; opacity: 0; transform: scale(.8); - transition-property: opacity, transform, visibility; - transition-duration: .2s; - transition-timing-function: cubic-bezier(.4, 0, .2, 1); + transition: opacity var(--btn-menu-transition), transform var(--btn-menu-transition), visibility var(--btn-menu-transition); font-size: 16px; body.animation-level-0 & { diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index 695ff791..7e38db68 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -1130,9 +1130,9 @@ $chat-helper-size: 39px; } } - /* .bubbles.is-chat-input-hidden & { - padding-bottom: 55px; - } */ + .bubbles.is-chat-input-hidden & { + margin-bottom: 1.25rem; + } &:not(.is-channel), &.is-chat { .message { diff --git a/src/scss/partials/_emojiDropdown.scss b/src/scss/partials/_emojiDropdown.scss index 2b5065ba..556c9a53 100644 --- a/src/scss/partials/_emojiDropdown.scss +++ b/src/scss/partials/_emojiDropdown.scss @@ -25,8 +25,7 @@ box-shadow: 0px 5px 10px 5px rgba(16, 35, 47, .14); z-index: 3; border-radius: 10px; - transition: transform .2s, opacity .2s; - transition-timing-function: cubic-bezier(.4, 0, .2, 1); + transition: transform var(--esg-transition), opacity var(--esg-transition); transform: scale(0); opacity: 0; transform-origin: 0 100%; diff --git a/src/scss/partials/_input.scss b/src/scss/partials/_input.scss index 4fc6c2be..c1f0c07a 100644 --- a/src/scss/partials/_input.scss +++ b/src/scss/partials/_input.scss @@ -95,8 +95,13 @@ @include hover() { &:not(:focus):not(.error):not(.valid) { //border-color: var(--color-gray); - border-color: #000; + //border-color: #000; + border-color: var(--primary-color); transition: .2s border-color; + + & ~ label { + color: var(--primary-color); + } } /* &:not(:focus):not(.error):not(.valid) ~ label { diff --git a/src/scss/partials/_rightSidebar.scss b/src/scss/partials/_rightSidebar.scss index d76076a4..28103766 100644 --- a/src/scss/partials/_rightSidebar.scss +++ b/src/scss/partials/_rightSidebar.scss @@ -99,7 +99,7 @@ } } -#shared-media-container { +.shared-media-container { /* .search-super { top: 100%; min-height: calc((var(--vh, 1vh) * 100) - 100% - 56px); @@ -441,6 +441,26 @@ } */ } } + + &-content-members { + .chatlist { + padding-top: .5rem; + padding-bottom: .5rem; + + li { + padding: .75rem; + } + + .user-caption { + padding-left: .75rem; + } + + .dialog-subtitle { + font-size: .875rem; + margin-top: -.375rem; + } + } + } } #search-container { diff --git a/src/scss/partials/_ripple.scss b/src/scss/partials/_ripple.scss index 6a8442f4..051ce168 100644 --- a/src/scss/partials/_ripple.scss +++ b/src/scss/partials/_ripple.scss @@ -48,7 +48,7 @@ } &__circle { - background-color: rgba(0, 0, 0, .08); + background-color: var(--ripple-color); display: block; position: absolute; transform: scale(0); diff --git a/src/scss/partials/popups/_createPoll.scss b/src/scss/partials/popups/_createPoll.scss index 362f8942..e312eb03 100644 --- a/src/scss/partials/popups/_createPoll.scss +++ b/src/scss/partials/popups/_createPoll.scss @@ -76,10 +76,6 @@ padding: 0 .5rem .5rem; } - hr { - border-top: 1px solid #edeff1; - } - .subtitle { margin-top: .875rem; font-size: .875rem; diff --git a/src/scss/partials/popups/_popup.scss b/src/scss/partials/popups/_popup.scss index 2905fb14..3016d235 100644 --- a/src/scss/partials/popups/_popup.scss +++ b/src/scss/partials/popups/_popup.scss @@ -5,7 +5,6 @@ */ .popup { - --transition-time: .15s; position: fixed!important; left: 0; top: 0; @@ -19,7 +18,10 @@ box-shadow: none; opacity: 0; visibility: hidden; - transition: opacity var(--transition-time) ease-in-out, visibility 0s var(--transition-time) ease-in-out; + transition-property: opacity, visibility; + transition-duration: var(--popup-transition-time), 0s; + transition-delay: 0s, var(--popup-transition-time); + transition-timing-function: var(--popup-transition-function); overflow: auto; display: flex; @@ -30,7 +32,7 @@ &.active { opacity: 1; visibility: visible; - transition: opacity var(--transition-time) ease-in-out, visibility 0s 0s ease-in-out; + transition-delay: 0s, 0s; z-index: 4; .popup-container { @@ -52,7 +54,7 @@ padding: 1rem; transform: translate3d(0, 3rem, 0); backface-visibility: hidden; - transition: transform var(--transition-time) ease-in-out; + transition: transform var(--popup-transition-time) var(--popup-transition-function); display: flex; flex-direction: column; overflow: hidden; diff --git a/src/scss/style.scss b/src/scss/style.scss index 4fda7f73..545e13c1 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -46,6 +46,10 @@ $chat-padding-handhelds: .5rem; --layer-transition: .2s ease-in-out; --slide-header-transition: .4s ease-in-out; --tabs-transition: .25s ease-in-out; + --btn-menu-transition: .2s cubic-bezier(.4, 0, .2, 1); + --esg-transition: var(--btn-menu-transition); + --popup-transition-function: cubic-bezier(.4, 0, .2, 1); + --popup-transition-time: .15s; //--layer-transition: .3s cubic-bezier(.33, 1, .68, 1); //--layer-transition: none; --btn-corner-transition: .2s cubic-bezier(.34, 1.56, .64, 1); @@ -137,6 +141,7 @@ $chat-padding-handhelds: .5rem; --chatlist-pinned-color: #a2abb2; --badge-text-color: #fff; --link-color: #00488f; + --ripple-color: rgba(0, 0, 0, .08); --message-background-color: var(--surface-color); --message-checkbox-color: #61c642; @@ -177,6 +182,7 @@ html.night { --chatlist-pinned-color: var(--secondary-color); --badge-text-color: #fff; --link-color: var(--primary-color); + --ripple-color: rgba(255, 255, 255, .08); --message-background-color: var(--surface-color); --message-checkbox-color: var(--primary-color);