diff --git a/src/components/chat/selection.ts b/src/components/chat/selection.ts index 61b8eb93..a62766b2 100644 --- a/src/components/chat/selection.ts +++ b/src/components/chat/selection.ts @@ -26,8 +26,8 @@ import blurActiveElement from "../../helpers/dom/blurActiveElement"; import { cancelEvent } from "../../helpers/dom/cancelEvent"; import cancelSelection from "../../helpers/dom/cancelSelection"; import getSelectedText from "../../helpers/dom/getSelectedText"; +import rootScope from "../../lib/rootScope"; -const MAX_SELECTION_LENGTH = 100; //const MIN_CLICK_MOVE = 32; // minimum bubble height export default class ChatSelection { @@ -473,7 +473,7 @@ export default class ChatSelection { if(found) { this.selectedMids.delete(mid); } else { - const diff = MAX_SELECTION_LENGTH - this.selectedMids.size - 1; + const diff = rootScope.config.forwarded_count_max - this.selectedMids.size - 1; if(diff < 0) { toast(I18n.format('Chat.Selection.LimitToast', true)); return; diff --git a/src/components/dialogsContextMenu.ts b/src/components/dialogsContextMenu.ts index 6400b0d1..36c88a84 100644 --- a/src/components/dialogsContextMenu.ts +++ b/src/components/dialogsContextMenu.ts @@ -14,6 +14,10 @@ import PopupDeleteDialog from "./popups/deleteDialog"; import { i18n } from "../lib/langPack"; import findUpTag from "../helpers/dom/findUpTag"; import appNotificationsManager from "../lib/appManagers/appNotificationsManager"; +import PopupPeer from "./popups/peer"; +import AppChatFoldersTab from "./sidebarLeft/tabs/chatFolders"; +import appSidebarLeft from "./sidebarLeft"; +import { toastNew } from "./toast"; export default class DialogsContextMenu { private element: HTMLElement; @@ -101,7 +105,27 @@ export default class DialogsContextMenu { }; private onPinClick = () => { - appMessagesManager.toggleDialogPin(this.selectedId, this.filterId); + appMessagesManager.toggleDialogPin(this.selectedId, this.filterId).catch(err => { + if(err.type === 'PINNED_DIALOGS_TOO_MUCH') { + if(this.filterId >= 1) { + toastNew({langPackKey: 'PinFolderLimitReached'}); + } else { + new PopupPeer('pinned-dialogs-too-much', { + buttons: [{ + langKey: 'OK', + isCancel: true + }, { + langKey: 'FiltersSetupPinAlert', + callback: () => { + new AppChatFoldersTab(appSidebarLeft).open(); + } + }], + descriptionLangKey: 'PinToTopLimitReached2', + descriptionLangArgs: [i18n('Chats', [rootScope.config.pinned_dialogs_count_max])] + }).show(); + } + } + }); }; private onUnmuteClick = () => { diff --git a/src/components/languageChangeButton.ts b/src/components/languageChangeButton.ts index 94adac0d..a798bb9c 100644 --- a/src/components/languageChangeButton.ts +++ b/src/components/languageChangeButton.ts @@ -26,7 +26,7 @@ rootScope.addEventListener('language_change', () => { function getLang(): Promise<[Config.config, LangPackString[], LangPackDifference.langPackDifference]> { if(cachedPromise) return cachedPromise; - return cachedPromise = apiManager.invokeApiCacheable('help.getConfig').then(config => { + return cachedPromise = apiManager.getConfig().then(config => { if(config.suggested_lang_code !== I18n.lastRequestedLangCode) { //I18n.loadLangPack(config.suggested_lang_code); diff --git a/src/components/popups/newMedia.ts b/src/components/popups/newMedia.ts index e7e97307..a0c1ab94 100644 --- a/src/components/popups/newMedia.ts +++ b/src/components/popups/newMedia.ts @@ -19,6 +19,7 @@ import appDownloadManager from "../../lib/appManagers/appDownloadManager"; import calcImageInBox from "../../helpers/calcImageInBox"; import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed"; import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd"; +import rootScope from "../../lib/rootScope"; type SendFileParams = Partial<{ file: File, @@ -30,8 +31,6 @@ type SendFileParams = Partial<{ duration: number }>; -const MAX_LENGTH_CAPTION = 1024; - // TODO: .gif upload as video export default class PopupNewMedia extends PopupElement { @@ -87,7 +86,7 @@ export default class PopupNewMedia extends PopupElement { placeholder: 'PreviewSender.CaptionPlaceholder', label: 'Caption', name: 'photo-caption', - maxLength: MAX_LENGTH_CAPTION, + maxLength: rootScope.config.caption_length_max, showLengthOn: 80 }); this.input = this.inputField.input; @@ -150,7 +149,7 @@ export default class PopupNewMedia extends PopupElement { } let caption = this.inputField.value; - if(caption.length > MAX_LENGTH_CAPTION) { + if(caption.length > rootScope.config.caption_length_max) { toast(I18n.format('Error.PreviewSender.CaptionTooLong', true)); return; } diff --git a/src/components/popups/peer.ts b/src/components/popups/peer.ts index 252f7f59..c0a42d9e 100644 --- a/src/components/popups/peer.ts +++ b/src/components/popups/peer.ts @@ -36,7 +36,7 @@ export default class PopupPeer extends PopupElement { this.header.prepend(avatarEl); } - if(options.descriptionLangKey) this.title.append(i18n(options.titleLangKey, options.titleLangArgs)); + if(options.titleLangKey || !options.title) this.title.append(i18n(options.titleLangKey || 'AppName', options.titleLangArgs)); else this.title.innerText = options.title || ''; let p = document.createElement('p'); diff --git a/src/lang.ts b/src/lang.ts index 2aee3f4d..077c50f3 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -518,6 +518,11 @@ const lang = { "AddMembersAlertCountText": "Are you sure you want to add %1$s to **%2$s**?", "AddMembersForwardMessages": "Show the last 100 messages to the new members", "AddOneMemberForwardMessages": "Show the last 100 messages to **%1$s**", + "PinToTopLimitReached2": "Sorry, you can only pin %1$s to the top in the main list. More chats can be pinned in Chat Folders and your Archive.", + "FiltersSetupPinAlert": "Set Up Folders", + "AppName": "Telegram", + "OK": "OK", + "PinFolderLimitReached": "Sorry, you can\'t pin any more chats to the top.", // * macos "AccountSettings.Filters": "Chat Folders", diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 817d4b82..dbdb7731 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -438,7 +438,7 @@ export class AppMessagesManager { options.replyToMsgId = options.threadId; } - const MAX_LENGTH = 4096; + const MAX_LENGTH = rootScope.config.message_length_max; if(text.length > MAX_LENGTH) { const splitted = splitStringByLength(text, MAX_LENGTH); text = splitted[0]; @@ -2913,6 +2913,11 @@ export class AppMessagesManager { public toggleDialogPin(peerId: number, filterId?: number) { if(filterId > 1) { return this.filtersStorage.toggleDialogPin(peerId, filterId); + } else { + const max = filterId === 1 ? rootScope.config.pinned_infolder_count_max : rootScope.config.pinned_dialogs_count_max; + if(this.dialogsStorage.getPinnedOrders(filterId).length >= max) { + return Promise.reject({type: 'PINNED_DIALOGS_TOO_MUCH'}); + } } const dialog = this.getDialogOnly(peerId); @@ -3017,11 +3022,12 @@ export class AppMessagesManager { } // * second rule for saved messages, because there is no 'out' flag - if(message.pFlags.out || this.getMessagePeer(message) === appUsersManager.getSelf().id) { + if(/* message.pFlags.out || */this.getMessagePeer(message) === appUsersManager.getSelf().id) { return true; } - if((message.date < (tsNow(true) - (2 * 86400)) && message.media?._ !== 'messageMediaPoll') || !message.pFlags.out) { + if((message.date < (tsNow(true) - rootScope.config.edit_time_limit) && + message.media?._ !== 'messageMediaPoll') || !message.pFlags.out) { return false; } diff --git a/src/lib/mtproto/mtprotoworker.ts b/src/lib/mtproto/mtprotoworker.ts index fd6801ea..722bd43e 100644 --- a/src/lib/mtproto/mtprotoworker.ts +++ b/src/lib/mtproto/mtprotoworker.ts @@ -7,7 +7,7 @@ import type { LocalStorageProxyTask, LocalStorageProxyTaskResponse } from '../localStorage'; //import type { LocalStorageProxyDeleteTask, LocalStorageProxySetTask } from '../storage'; import type { Awaited, InvokeApiOptions, WorkerTaskVoidTemplate } from '../../types'; -import type { InputFile, MethodDeclMap } from '../../layer'; +import type { Config, InputFile, MethodDeclMap } from '../../layer'; import MTProtoWorker from 'worker-loader!./mtproto.worker'; //import './mtproto.worker'; import { isObject } from '../../helpers/object'; @@ -99,6 +99,8 @@ export class ApiManagerProxy extends CryptoWorkerMethods { private postMessagesWaiting: any[][] = []; + private getConfigPromise: Promise; + constructor() { super(); this.log('constructor'); @@ -210,6 +212,10 @@ export class ApiManagerProxy extends CryptoWorkerMethods { /// #if !MTPROTO_SW this.registerWorker(); /// #endif + + setTimeout(() => { + this.getConfig(); + }, 5000); } public isServiceWorkerOnline() { @@ -597,6 +603,14 @@ export class ApiManagerProxy extends CryptoWorkerMethods { public forceReconnect() { this.postMessage({type: 'forceReconnect'}); } + + public getConfig() { + if(this.getConfigPromise) return this.getConfigPromise; + return this.getConfigPromise = this.invokeApi('help.getConfig').then(config => { + rootScope.config = config; + return config; + }); + } } const apiManagerProxy = new ApiManagerProxy(); diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 66c485e6..9ffe1da4 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import type { Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, ConstructorDeclMap } from "../layer"; +import type { Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, ConstructorDeclMap, Config } from "../layer"; import type { MyDocument } from "./appManagers/appDocsManager"; import type { AppMessagesManager, Dialog, MessagesStorage } from "./appManagers/appMessagesManager"; import type { Poll, PollResults } from "./appManagers/appPollsManager"; @@ -144,6 +144,14 @@ export class RootScope extends EventListenerBase<{ public settings: State['settings']; public peerId = 0; public systemTheme: Theme['name']; + public config: Partial = { + forwarded_count_max: 100, + edit_time_limit: 86400 * 2, + pinned_dialogs_count_max: 5, + pinned_infolder_count_max: 100, + message_length_max: 4096, + caption_length_max: 1024, + }; constructor() { super(); diff --git a/src/lib/storages/dialogs.ts b/src/lib/storages/dialogs.ts index c61d0b43..db5c96f0 100644 --- a/src/lib/storages/dialogs.ts +++ b/src/lib/storages/dialogs.ts @@ -146,6 +146,10 @@ export default class DialogsStorage { this.pinnedOrders[folderId] = []; } + public getPinnedOrders(folderId: number) { + return this.pinnedOrders[folderId]; + } + public getOffsetDate(folderId: number) { return this.dialogsOffsetDate[folderId] || 0; } diff --git a/src/lib/storages/filters.ts b/src/lib/storages/filters.ts index 9843a27f..edf08680 100644 --- a/src/lib/storages/filters.ts +++ b/src/lib/storages/filters.ts @@ -182,6 +182,10 @@ export default class FiltersStorage { public toggleDialogPin(peerId: number, filterId: number) { const filter = this.filters[filterId]; + if(filter.pinned_peers.length >= this.rootScope.config.pinned_infolder_count_max) { + return Promise.reject({type: 'PINNED_DIALOGS_TOO_MUCH'}); + } + const wasPinned = filter.pinned_peers.findAndSplice(p => p === peerId); if(!wasPinned) { filter.pinned_peers.unshift(peerId);