From 23960bf5e078fecb15b1be9c1311ca3fba2b39e4 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Thu, 2 Mar 2023 19:51:37 +0400 Subject: [PATCH] Stickers lite mode Common chats tab --- src/components/animationIntersector.ts | 29 +++++ src/components/appSearchSuper..ts | 100 +++++++++++++----- src/components/chat/bubbles.ts | 6 ++ src/components/lazyLoadQueue.ts | 8 ++ .../sidebarLeft/tabs/powerSaving.ts | 2 +- .../sidebarRight/tabs/sharedMedia.ts | 4 + src/components/sortedUserList.ts | 16 ++- src/lib/appManagers/appImManager.ts | 4 +- src/lib/appManagers/appUsersManager.ts | 15 +++ src/lib/rlottie/lottieLoader.ts | 31 ------ src/scss/partials/_audio.scss | 1 + src/scss/partials/_rightSidebar.scss | 3 +- 12 files changed, 157 insertions(+), 62 deletions(-) diff --git a/src/components/animationIntersector.ts b/src/components/animationIntersector.ts index fe462e88..42a460ad 100644 --- a/src/components/animationIntersector.ts +++ b/src/components/animationIntersector.ts @@ -337,6 +337,35 @@ export class AnimationIntersector { if(lock) this.lockIntersectionGroup(group); else this.unlockIntersectionGroup(group); } + + public setAutoplay(play: boolean, liteModeKey: LiteModeKey) { + let changed = false; + this.byPlayer.forEach((animationItem, animation) => { + if(animationItem.liteModeKey === liteModeKey) { + changed = true; + animation.autoplay = play ? !!+animationItem.el.dataset.stickerPlay : false; + animation.loop = play ? !!+animationItem.el.dataset.stickerLoop : false; + } + }); + + return changed; + } + + public setLoop(loop: boolean) { + let changed = false; + this.byPlayer.forEach((animationItem, animation) => { + if(!!+animationItem.el.dataset.stickerLoop && animation.loop !== loop) { + changed = true; + animation.loop = loop; + + // if(animation._autoplay && animation.autoplay !== animation._autoplay) { + animation.autoplay = !!+animationItem.el.dataset.stickerPlay; + // } + } + }); + + return changed; + } } const animationIntersector = new AnimationIntersector(); diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index b20dbc46..ec6091f8 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -18,7 +18,7 @@ import useHeavyAnimationCheck, {getHeavyAnimationPromise} from '../hooks/useHeav import I18n, {LangPackKey, i18n} from '../lib/langPack'; import findUpClassName from '../helpers/dom/findUpClassName'; import {getMiddleware, Middleware} from '../helpers/middleware'; -import {ChannelParticipant, Chat, ChatFull, ChatParticipant, ChatParticipants, Document, Message, MessageMedia, Photo, WebPage} from '../layer'; +import {ChannelParticipant, Chat, ChatFull, ChatParticipant, ChatParticipants, Document, Message, MessageMedia, Photo, User, WebPage} from '../layer'; import SortedUserList from './sortedUserList'; import findUpTag from '../helpers/dom/findUpTag'; import appSidebarRight from './sidebarRight'; @@ -75,6 +75,7 @@ import wrapPhoto from './wrappers/photo'; import wrapVideo from './wrappers/video'; import noop from '../helpers/noop'; import wrapMediaSpoiler, {onMediaSpoilerClick} from './wrappers/mediaSpoiler'; +import filterAsync from '../helpers/array/filterAsync'; // const testScroll = false; @@ -92,7 +93,7 @@ export type SearchSuperContext = { maxDate?: number }; -export type SearchSuperMediaType = 'members' | 'media' | 'files' | 'links' | 'music' | 'chats' | 'voice'; +export type SearchSuperMediaType = 'members' | 'media' | 'files' | 'links' | 'music' | 'chats' | 'voice' | 'groups'; export type SearchSuperMediaTab = { inputFilter: SearchSuperType, name: LangPackKey, @@ -1163,11 +1164,12 @@ export default class AppSearchSuper { } private async loadMembers(mediaTab: SearchSuperMediaTab) { - const id = this.searchContext.peerId.toChatId(); + const chatId = mediaTab.type === 'members' ? this.searchContext.peerId.toChatId() : undefined; + const userId = mediaTab.type === 'groups' ? this.searchContext.peerId.toUserId() : undefined; const middleware = this.middleware.get(); let promise: Promise; - const renderParticipants = async(participants: (ChatParticipant | ChannelParticipant)[]) => { + const renderParticipants = async(participants: (ChatParticipant | ChannelParticipant | Chat)[]) => { if(this.loadMutex) { await this.loadMutex; @@ -1178,7 +1180,7 @@ export default class AppSearchSuper { let membersList = this.membersList; if(!membersList) { - membersList = new SortedUserList({ + membersList = this.membersList = new SortedUserList({ lazyLoadQueue: this.lazyLoadQueue, rippleEnabled: false, managers: this.managers @@ -1203,28 +1205,53 @@ export default class AppSearchSuper { this.afterPerforming(1, mediaTab.contentTab); } - for(const participant of participants) { - const peerId = getParticipantPeerId(participant); - if(peerId.isAnyChat()) { - continue; - } - - const user = await this.managers.appUsersManager.getUser(peerId); - if(!middleware()) { + const peerIds = participants.map((participant) => { + const peerId = userId ? (participant as Chat.chat).id.toPeerId(true) : getParticipantPeerId(participant as ChannelParticipant); + if(chatId ? peerId.isAnyChat() : peerId.isUser()) { return; } - if(user.pFlags.deleted) { - continue; + return peerId; + }).filter(Boolean); + + const filtered = await filterAsync(peerIds, async(peerId) => { + const peer: User | Chat = await this.managers.appPeersManager.getPeer(peerId); + if(!middleware()) { + return false; } + if(!peer || (peer as User.user).pFlags.deleted) { + return false; + } + + return true; + }); + + for(const peerId of filtered) { membersList.add(peerId); } }; - if(await this.managers.appChatsManager.isChannel(id)) { + if(userId) { const LOAD_COUNT = !this.membersList ? 50 : 200; - promise = this.managers.appProfileManager.getChannelParticipants(id, undefined, LOAD_COUNT, this.nextRates[mediaTab.inputFilter]).then((participants) => { + promise = this.managers.appUsersManager.getCommonChats(userId, LOAD_COUNT, this.nextRates[mediaTab.inputFilter]).then((messagesChats) => { + if(!middleware()) { + return; + } + + // const list = mediaTab.contentTab.firstElementChild as HTMLUListElement; + const lastChat = messagesChats.chats[messagesChats.chats.length - 1]; + this.nextRates[mediaTab.inputFilter] = lastChat?.id as number; + + if(messagesChats.chats.length < LOAD_COUNT) { + this.loaded[mediaTab.inputFilter] = true; + } + + return renderParticipants(messagesChats.chats); + }); + } else if(await this.managers.appChatsManager.isChannel(chatId)) { + const LOAD_COUNT = !this.membersList ? 50 : 200; + promise = this.managers.appProfileManager.getChannelParticipants(chatId, undefined, LOAD_COUNT, this.nextRates[mediaTab.inputFilter]).then((participants) => { if(!middleware()) { return; } @@ -1239,7 +1266,7 @@ export default class AppSearchSuper { return renderParticipants(participants.participants); }); } else { - promise = this.managers.appProfileManager.getChatFull(id).then((chatFull) => { + promise = this.managers.appProfileManager.getChatFull(chatId).then((chatFull) => { if(!middleware()) { return; } @@ -1271,7 +1298,7 @@ export default class AppSearchSuper { return this.loadPromises[type]; } - if(mediaTab.type === 'members') { + if(mediaTab.type === 'members' || mediaTab.type === 'groups') { return this.loadMembers(mediaTab); } @@ -1403,9 +1430,10 @@ export default class AppSearchSuper { const mediaTabs = this.mediaTabs.filter((mediaTab) => mediaTab.inputFilter !== 'inputMessagesFilterEmpty'); const filters = mediaTabs.map((mediaTab) => ({_: mediaTab.inputFilter})); - const [counters, canViewMembers] = await Promise.all([ + const [counters, canViewMembers, canViewGroups] = await Promise.all([ this.managers.appMessagesManager.getSearchCounters(peerId, filters, undefined, threadId), - this.canViewMembers() + this.canViewMembers(), + this.canViewGroups() ]); if(!middleware()) { @@ -1439,11 +1467,26 @@ export default class AppSearchSuper { }); const membersTab = this.mediaTabsMap.get('members'); - membersTab.menuTab.classList.toggle('hide', !canViewMembers); + + const a: [SearchSuperMediaTab, boolean][] = [ + [membersTab, canViewMembers], + [this.mediaTabsMap.get('groups'), canViewGroups] + ]; + + a.forEach(([tab, value]) => { + if(!tab) { + return; + } + + tab.menuTab.classList.toggle('hide', !value); + + if(value) { + ++count; + } + }); if(canViewMembers) { firstMediaTab = membersTab; - ++count; } this.container.classList.toggle('hide', !firstMediaTab); @@ -1479,6 +1522,8 @@ export default class AppSearchSuper { if(peerId.isUser()) { findAndSplice(toLoad, (mediaTab) => mediaTab.type === 'members'); + } else { + findAndSplice(toLoad, (mediaTab) => mediaTab.type === 'groups'); } if(!toLoad.length) { @@ -1549,7 +1594,7 @@ export default class AppSearchSuper { public canViewMembers() { const {peerId} = this.searchContext; const isAnyChat = peerId.isAnyChat(); - if(!isAnyChat) return Promise.resolve(false); + if(!isAnyChat || !this.mediaTabsMap.has('members')) return Promise.resolve(false); const chatId = peerId.toChatId(); return Promise.all([ this.managers.appChatsManager.isBroadcast(chatId), @@ -1560,6 +1605,13 @@ export default class AppSearchSuper { }); } + public async canViewGroups() { + const {peerId} = this.searchContext; + if(!peerId.isUser() || !this.mediaTabsMap.has('groups')) return false; + const userFull = await this.managers.appProfileManager.getProfile(peerId.toUserId()); + return !!userFull.common_chats_count; + } + public cleanup() { this.loadPromises = {}; this.loaded = {}; diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index b660f521..562c631e 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -3510,6 +3510,12 @@ export default class ChatBubbles { restoreScroll?.(); + pause(!this.chat.setPeerPromise ? 0 : 1000) + .then(() => getHeavyAnimationPromise()) + .then(() => { + this.lazyLoadQueue.setAllSeen(); + }); + // this.setStickyDateManually(); }; diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts index 38e33032..d2a81d15 100644 --- a/src/components/lazyLoadQueue.ts +++ b/src/components/lazyLoadQueue.ts @@ -64,4 +64,12 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector { return true; } + + public setAllSeen() { + this.queue.forEach((item) => { + item.wasSeen = true; + }); + + this.setProcessQueueTimeout(); + } } diff --git a/src/components/sidebarLeft/tabs/powerSaving.ts b/src/components/sidebarLeft/tabs/powerSaving.ts index 54148991..02963ec0 100644 --- a/src/components/sidebarLeft/tabs/powerSaving.ts +++ b/src/components/sidebarLeft/tabs/powerSaving.ts @@ -39,7 +39,7 @@ export default class AppPowerSavingTab extends SliderSuperTab { 'all', 'video', 'gif', - // ['stickers', ['stickers_panel', 'stickers_chat']], + ['stickers', ['stickers_panel', 'stickers_chat']], // ['emoji', ['emoji_panel', 'emoji_messages']], ['effects', ['effects_reactions', 'effects_premiumstickers', 'effects_emoji']], ['chat', ['chat_background', 'chat_spoilers']], diff --git a/src/components/sidebarRight/tabs/sharedMedia.ts b/src/components/sidebarRight/tabs/sharedMedia.ts index bc81cc85..3e78ddb9 100644 --- a/src/components/sidebarRight/tabs/sharedMedia.ts +++ b/src/components/sidebarRight/tabs/sharedMedia.ts @@ -224,6 +224,10 @@ export default class AppSharedMediaTab extends SliderSuperTab { inputFilter: 'inputMessagesFilterRoundVoice', name: 'SharedVoiceTab2', type: 'voice' + }, { + inputFilter: 'inputMessagesFilterEmpty', + name: 'ChatList.Filter.Groups', + type: 'groups' }], scrollable: this.scrollable, onChangeTab: (mediaTab) => { diff --git a/src/components/sortedUserList.ts b/src/components/sortedUserList.ts index 81d9e6ca..9a5b106b 100644 --- a/src/components/sortedUserList.ts +++ b/src/components/sortedUserList.ts @@ -15,6 +15,7 @@ import safeAssign from '../helpers/object/safeAssign'; import {AppManagers} from '../lib/appManagers/managers'; import getUserStatusString from './wrappers/getUserStatusString'; import type LazyLoadQueue from './lazyLoadQueue'; +import getChatMembersString from './wrappers/getChatMembersString'; interface SortedUser extends SortedElementBase { dom: DialogDom @@ -47,14 +48,19 @@ export default class SortedUserList extends SortedList { managers: SortedUserList['managers'] }) { super({ - getIndex: options.getIndex || ((element) => this.managers.appUsersManager.getUserStatusForSort(element.id)), + getIndex: options.getIndex || ((element) => element.id.isAnyChat() ? 0 : this.managers.appUsersManager.getUserStatusForSort(element.id)), onDelete: (element) => { element.dom.listEl.remove(); this.onListLengthChange && this.onListLengthChange(); }, onUpdate: options.onUpdate || (async(element) => { - const status = getUserStatusString(await this.managers.appUsersManager.getUser(element.id)); - replaceContent(element.dom.lastMessageSpan, status); + if(element.id.isAnyChat()) { + const status = await getChatMembersString(element.id.toChatId(), this.managers); + replaceContent(element.dom.lastMessageSpan, status); + } else { + const status = getUserStatusString(await this.managers.appUsersManager.getUser(element.id)); + replaceContent(element.dom.lastMessageSpan, status); + } }), onSort: (element, idx) => { const willChangeLength = element.dom.listEl.parentElement !== this.list; @@ -82,6 +88,10 @@ export default class SortedUserList extends SortedList { }, updateElementWith: fastRaf, updateListWith: async(callback) => { + if(!Array.from(this.elements.values()).some((element) => element.id.isUser())) { + return callback(false); + } + if(!isInDOM(this.list)) { return callback(false); } diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 23941e32..48321f24 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -1677,8 +1677,8 @@ export class AppImManager extends EventListenerBase<{ }, liteMode.isAvailable('animations') ? 250 : 0, false, true); const c: LiteModeKey[] = ['stickers_chat', 'stickers_panel']; - const changedLoop = lottieLoader.setLoop(rootScope.settings.stickers.loop); - const changedAutoplay = !!c.filter((key) => lottieLoader.setAutoplay(liteMode.isAvailable(key), key)).length; + const changedLoop = animationIntersector.setLoop(rootScope.settings.stickers.loop); + const changedAutoplay = !!c.filter((key) => animationIntersector.setAutoplay(liteMode.isAvailable(key), key)).length; if(changedLoop || changedAutoplay) { animationIntersector.checkAnimations2(false); } diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index ad33471a..89043c33 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -1056,4 +1056,19 @@ export class AppUsersManager extends AppManager { public canSendToUser(userId: UserId) { return canSendToUser(this.getUser(userId)); } + + public getCommonChats(userId: UserId, limit = 100, maxId?: ChatId) { + return this.apiManager.invokeApiSingleProcess({ + method: 'messages.getCommonChats', + params: { + user_id: this.getUserInput(userId), + limit, + max_id: maxId ?? 0 + }, + processResult: (messagesChats) => { + this.appChatsManager.saveApiChats(messagesChats.chats); + return messagesChats; + } + }); + } } diff --git a/src/lib/rlottie/lottieLoader.ts b/src/lib/rlottie/lottieLoader.ts index 2a49c6d5..555a2754 100644 --- a/src/lib/rlottie/lottieLoader.ts +++ b/src/lib/rlottie/lottieLoader.ts @@ -46,37 +46,6 @@ export class LottieLoader { return null; } - public setAutoplay(play: boolean, liteModeKey: LiteModeKey) { - let changed = false; - for(const i in this.players) { - const player = this.players[i]; - if(player.liteModeKey === liteModeKey) { - changed = true; - player.autoplay = play ? !!+player.el[0].dataset.stickerPlay : false; - player.loop = play ? !!+player.el[0].dataset.stickerLoop : false; - } - } - - return changed; - } - - public setLoop(loop: boolean) { - let changed = false; - for(const i in this.players) { - const player = this.players[i]; - if(player._loop && player.loop !== loop) { - changed = true; - player.loop = loop; - - // if(player._autoplay && player.autoplay !== player._autoplay) { - player.autoplay = player._autoplay; - // } - } - } - - return changed; - } - public loadLottieWorkers() { if(this.loadPromise) { return this.loadPromise; diff --git a/src/scss/partials/_audio.scss b/src/scss/partials/_audio.scss index afad156b..73f6b8dc 100644 --- a/src/scss/partials/_audio.scss +++ b/src/scss/partials/_audio.scss @@ -581,6 +581,7 @@ .progress-line { margin-top: 4px; margin-inline-start: 0; + min-width: 11.25rem; &__filled:after { opacity: 0; diff --git a/src/scss/partials/_rightSidebar.scss b/src/scss/partials/_rightSidebar.scss index 7c89994c..165a557d 100644 --- a/src/scss/partials/_rightSidebar.scss +++ b/src/scss/partials/_rightSidebar.scss @@ -604,7 +604,8 @@ } } - &-content-members { + &-content-members, + &-content-groups { .chatlist { padding-top: .5rem; padding-bottom: .5rem;