From b148a60c8c79a414c705c80012fde426f93283db Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Mon, 1 Aug 2022 11:22:44 +0200 Subject: [PATCH] Misc folder management fixes --- .../sidebarLeft/tabs/chatFolders.ts | 73 +++++++++++++------ .../sidebarLeft/tabs/includedChats.ts | 31 ++++++-- src/lib/appManagers/appUsersManager.ts | 1 - src/lib/mtproto/api_methods.ts | 1 + src/lib/mtproto/mtproto_config.ts | 3 + src/lib/rootScope.ts | 15 +++- src/lib/storages/filters.ts | 3 +- 7 files changed, 89 insertions(+), 38 deletions(-) diff --git a/src/components/sidebarLeft/tabs/chatFolders.ts b/src/components/sidebarLeft/tabs/chatFolders.ts index 43a2f9aa..b6d80ed2 100644 --- a/src/components/sidebarLeft/tabs/chatFolders.ts +++ b/src/components/sidebarLeft/tabs/chatFolders.ts @@ -4,12 +4,12 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ +import type { MyDialogFilter } from "../../../lib/storages/filters"; +import type { DialogFilterSuggested } from "../../../layer"; +import type _rootScope from "../../../lib/rootScope"; import { SliderSuperTab } from "../../slider"; import lottieLoader, { LottieLoader } from "../../../lib/rlottie/lottieLoader"; import { toast } from "../../toast"; -import type { MyDialogFilter } from "../../../lib/storages/filters"; -import type { DialogFilterSuggested, DialogFilter } from "../../../layer"; -import type _rootScope from "../../../lib/rootScope"; import Button from "../../button"; import rootScope from "../../../lib/rootScope"; import AppEditFolderTab from "./editFolder"; @@ -22,6 +22,7 @@ import positionElementByIndex from "../../../helpers/dom/positionElementByIndex" import RLottiePlayer from "../../../lib/rlottie/rlottiePlayer"; import wrapEmojiText from "../../../lib/richTextProcessor/wrapEmojiText"; import { FOLDER_ID_ALL, FOLDER_ID_ARCHIVE, REAL_FOLDERS } from "../../../lib/mtproto/mtproto_config"; +import replaceContent from "../../../helpers/dom/replaceContent"; export default class AppChatFoldersTab extends SliderSuperTab { private createFolderBtn: HTMLElement; @@ -33,7 +34,12 @@ export default class AppChatFoldersTab extends SliderSuperTab { private filtersRendered: {[filterId: number]: Row} = {}; private loadAnimationPromise: ReturnType; - private async renderFolder(dialogFilter: DialogFilterSuggested | MyDialogFilter, container?: HTMLElement, row?: Row) { + private async renderFolder( + dialogFilter: DialogFilterSuggested | MyDialogFilter, + container?: HTMLElement, + row?: Row, + append?: boolean + ) { let filter: MyDialogFilter; let description = ''; let d: HTMLElement[] = []; @@ -86,14 +92,12 @@ export default class AppChatFoldersTab extends SliderSuperTab { }); if(d.length) { - join(d).forEach((el) => { - row.subtitle.append(el); - }); + row.subtitle.append(...join(d)); } if(dialogFilter._ === 'dialogFilter') { const filterId = filter.id; - if(!this.filtersRendered.hasOwnProperty(filter.id) && filter.id !== FOLDER_ID_ALL) { + if(!this.filtersRendered[filter.id] && filter.id !== FOLDER_ID_ALL) { attachClickEvent(row.container, async() => { this.slider.createTab(AppEditFolderTab).open(await this.managers.filtersStorage.getFilter(filterId)); }, {listenerSetter: this.listenerSetter}); @@ -102,18 +106,25 @@ export default class AppChatFoldersTab extends SliderSuperTab { this.filtersRendered[filter.id] = row; } } else { + if(filter.id !== FOLDER_ID_ALL) { + replaceContent(row.title, wrapEmojiText(filter.title)); + } + row.subtitle.textContent = ''; - join(d).forEach((el) => { - row.subtitle.append(el); - }); + row.subtitle.append(...join(d)); } div = row.container; - if((filter as MyDialogFilter).localId !== undefined) { - // ! header will be at 0 index - positionElementByIndex(div, div.parentElement || container, (filter as MyDialogFilter).localId); - } else if(container) container.append(div); + if(append) { + const localId = (filter as MyDialogFilter).localId; + if(localId !== undefined) { + // ! header will be at 0 index + positionElementByIndex(div, div.parentElement || container, localId); + } else if(container) { + container.append(div); + } + } return div; } @@ -166,17 +177,20 @@ export default class AppChatFoldersTab extends SliderSuperTab { continue; } - await this.renderFolder(filter, this.foldersSection.content); + await this.renderFolder(filter, this.foldersSection.content, undefined, true); } + this.toggleAllChats(); + onFiltersContainerUpdate(); }); this.listenerSetter.add(rootScope)('filter_update', async(filter) => { - if(this.filtersRendered.hasOwnProperty(filter.id)) { - await this.renderFolder(filter, null, this.filtersRendered[filter.id]); - } else { - await this.renderFolder(filter, this.foldersSection.content); + const filterRendered = this.filtersRendered[filter.id]; + if(filterRendered) { + await this.renderFolder(filter, null, filterRendered); + } else if(filter.id !== FOLDER_ID_ARCHIVE) { + await this.renderFolder(filter, this.foldersSection.content, undefined, true); } onFiltersContainerUpdate(); @@ -185,7 +199,8 @@ export default class AppChatFoldersTab extends SliderSuperTab { }); this.listenerSetter.add(rootScope)('filter_delete', (filter) => { - if(this.filtersRendered.hasOwnProperty(filter.id)) { + const filterRendered = this.filtersRendered[filter.id]; + if(filterRendered) { /* for(const suggested of this.suggestedFilters) { if(deepEqual(suggested.filter, filter)) { @@ -193,7 +208,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { } */ this.getSuggestedFilters(); - this.filtersRendered[filter.id].container.remove(); + filterRendered.container.remove(); delete this.filtersRendered[filter.id]; } @@ -201,12 +216,17 @@ export default class AppChatFoldersTab extends SliderSuperTab { }); this.listenerSetter.add(rootScope)('filter_order', (order) => { - order.forEach((filterId, idx) => { - const container = this.filtersRendered[filterId].container; + order.filter((filterId) => !!this.filtersRendered[filterId]).forEach((filterId, idx) => { + const filterRendered = this.filtersRendered[filterId]; + const container = filterRendered.container; positionElementByIndex(container, container.parentElement, idx + 1); // ! + 1 due to header }); }); + this.listenerSetter.add(rootScope)('premium_toggle', () => { + this.toggleAllChats(); + }); + this.loadAnimationPromise = lottieLoader.loadAnimationAsAsset({ container: this.stickerContainer, loop: false, @@ -234,6 +254,11 @@ export default class AppChatFoldersTab extends SliderSuperTab { }); } + private toggleAllChats() { + const filterRendered = this.filtersRendered[FOLDER_ID_ALL]; + filterRendered.container.classList.toggle('hide', !rootScope.premium); + } + private async canCreateFolder() { const [appConfig, filters] = await Promise.all([ this.managers.apiManager.getAppConfig(), diff --git a/src/components/sidebarLeft/tabs/includedChats.ts b/src/components/sidebarLeft/tabs/includedChats.ts index 2e05dbbf..e3d429fd 100644 --- a/src/components/sidebarLeft/tabs/includedChats.ts +++ b/src/components/sidebarLeft/tabs/includedChats.ts @@ -20,6 +20,8 @@ import forEachReverse from "../../../helpers/array/forEachReverse"; import setInnerHTML from "../../../helpers/dom/setInnerHTML"; import wrapEmojiText from "../../../lib/richTextProcessor/wrapEmojiText"; import { REAL_FOLDERS } from "../../../lib/mtproto/mtproto_config"; +import rootScope from "../../../lib/rootScope"; +import { MTAppConfig } from "../../../lib/mtproto/appConfig"; export default class AppIncludedChatsTab extends SliderSuperTab { private editFolderTab: AppEditFolderTab; @@ -31,6 +33,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab { private originalFilter: DialogFilter; private dialogsByFilters: Map>; + private limit: number; protected init() { this.content.remove(); @@ -106,14 +109,26 @@ export default class AppIncludedChatsTab extends SliderSuperTab { this.close(); }); + const onAppConfig = (appConfig: MTAppConfig) => { + this.limit = rootScope.premium ? appConfig.dialog_filters_chats_limit_premium : appConfig.dialog_filters_chats_limit_default; + }; + + this.listenerSetter.add(rootScope)('app_config', onAppConfig); + this.dialogsByFilters = new Map(); - return this.managers.filtersStorage.getDialogFilters().then(async(filters) => { - await Promise.all(filters.filter((filter) => !REAL_FOLDERS.has(filter.id)).map(async(filter) => { - const dialogs = await this.managers.dialogsStorage.getFolderDialogs(filter.id); - const peerIds = dialogs.map((d) => d.peerId); - this.dialogsByFilters.set(filter, new Set(peerIds)); - })); - }); + return Promise.all([ + this.managers.filtersStorage.getDialogFilters().then(async(filters) => { + await Promise.all(filters.filter((filter) => !REAL_FOLDERS.has(filter.id)).map(async(filter) => { + const dialogs = await this.managers.dialogsStorage.getFolderDialogs(filter.id); + const peerIds = dialogs.map((d) => d.peerId); + this.dialogsByFilters.set(filter, new Set(peerIds)); + })); + }), + + this.managers.apiManager.getAppConfig().then((appConfig) => { + onAppConfig(appConfig); + }) + ]); } checkbox(selected?: boolean) { @@ -223,7 +238,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab { let addedInitial = false; const _add = this.selector.add.bind(this.selector); this.selector.add = (peerId, title, scroll) => { - if(this.selector.selected.size >= 100 && addedInitial && !details[peerId]) { + if(this.selector.selected.size >= this.limit && addedInitial && !details[peerId]) { const el: HTMLInputElement = this.selector.list.querySelector(`[data-peer-id="${peerId}"] [type="checkbox"]`); if(el) { setTimeout(() => { diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index 90aee631..0af2032d 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -550,7 +550,6 @@ export class AppUsersManager extends AppManager { if(user.pFlags.self) { const isPremium = !!user.pFlags.premium; if(this.rootScope.premium !== isPremium) { - this.rootScope.premium = isPremium; this.rootScope.dispatchEvent('premium_toggle', isPremium); } } diff --git a/src/lib/mtproto/api_methods.ts b/src/lib/mtproto/api_methods.ts index 63002d0f..ca60a95c 100644 --- a/src/lib/mtproto/api_methods.ts +++ b/src/lib/mtproto/api_methods.ts @@ -241,6 +241,7 @@ export default abstract class ApiManagerMethods extends AppManager { this.appConfig = config; ignoreRestrictionReasons(config.ignore_restriction_reasons ?? []); + this.rootScope.dispatchEvent('app_config', config); return config; }); diff --git a/src/lib/mtproto/mtproto_config.ts b/src/lib/mtproto/mtproto_config.ts index 5cb055b5..dd3b4882 100644 --- a/src/lib/mtproto/mtproto_config.ts +++ b/src/lib/mtproto/mtproto_config.ts @@ -4,6 +4,8 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ +import type { MyDialogFilter } from "../storages/filters"; + /** * Legacy Webogram's format, don't change dcID to camelCase. date is timestamp */ @@ -20,3 +22,4 @@ export const BOT_START_PARAM = ''; export const FOLDER_ID_ALL: REAL_FOLDER_ID = 0; export const FOLDER_ID_ARCHIVE: REAL_FOLDER_ID = 1; export const REAL_FOLDERS: Set = new Set([FOLDER_ID_ALL, FOLDER_ID_ARCHIVE]); +export const START_LOCAL_ID = Math.max(...Array.from(REAL_FOLDERS)) + 1 as MyDialogFilter['localId']; diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 64ef3da5..eda7a123 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -21,6 +21,7 @@ import EventListenerBase from "../helpers/eventListenerBase"; import { MOUNT_CLASS_TO } from "../config/debug"; import MTProtoMessagePort from "./mtproto/mtprotoMessagePort"; import { IS_WORKER } from "../helpers/context"; +import { MTAppConfig } from "./mtproto/appConfig"; export type BroadcastEvents = { 'chat_full_update': ChatId, @@ -140,7 +141,9 @@ export type BroadcastEvents = { 'payment_sent': {peerId: PeerId, mid: number}, - 'premium_toggle': boolean + 'premium_toggle': boolean, + + 'app_config': MTAppConfig }; export type BroadcastEventsListeners = { @@ -148,8 +151,8 @@ export type BroadcastEventsListeners = { }; export class RootScope extends EventListenerBase { - public myId: PeerId = NULL_PEER_ID; - private connectionStatus: {[name: string]: ConnectionStatusChange} = {}; + public myId: PeerId; + private connectionStatus: {[name: string]: ConnectionStatusChange}; public settings: State['settings']; public managers: AppManagers; public premium: boolean; @@ -157,12 +160,18 @@ export class RootScope extends EventListenerBase { constructor() { super(); + this.myId = NULL_PEER_ID; + this.connectionStatus = {}; this.premium = false; this.addEventListener('user_auth', ({id}) => { this.myId = id.toPeerId(); }); + this.addEventListener('premium_toggle', (isPremium) => { + this.premium = isPremium; + }); + this.addEventListener('connection_status_change', (status) => { this.connectionStatus[status.name] = status; }); diff --git a/src/lib/storages/filters.ts b/src/lib/storages/filters.ts index 0973ed96..9644c453 100644 --- a/src/lib/storages/filters.ts +++ b/src/lib/storages/filters.ts @@ -11,7 +11,7 @@ import copy from "../../helpers/object/copy"; import { AppManager } from "../appManagers/manager"; import findAndSplice from "../../helpers/array/findAndSplice"; import assumeType from "../../helpers/assumeType"; -import { FOLDER_ID_ALL, FOLDER_ID_ARCHIVE, REAL_FOLDERS, REAL_FOLDER_ID } from "../mtproto/mtproto_config"; +import { FOLDER_ID_ALL, FOLDER_ID_ARCHIVE, REAL_FOLDERS, REAL_FOLDER_ID, START_LOCAL_ID } from "../mtproto/mtproto_config"; export type MyDialogFilter = DialogFilter.dialogFilter; @@ -21,7 +21,6 @@ const convertment = [ ['include_peers', 'includePeerIds'] ] as ['pinned_peers' | 'exclude_peers' | 'include_peers', 'pinnedPeerIds' | 'excludePeerIds' | 'includePeerIds'][]; -const START_LOCAL_ID = Math.max(...Array.from(REAL_FOLDERS)) + 1 as MyDialogFilter['localId']; const PREPENDED_FILTERS = REAL_FOLDERS.size; const LOCAL_FILTER: MyDialogFilter = {