From 3b486614df5fd0683be0bc550d99ca5242209e27 Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:15:30 +0100 Subject: [PATCH] Folders: Support custom emoji in title (#5385) Co-authored-by: Dmitry Kabanov Co-authored-by: Dmitry Kabanov <153344039+dmitrykabanovdev@users.noreply.github.com> --- src/api/gramjs/apiBuilders/chats.ts | 10 ++-- src/api/gramjs/gramjsBuilders/index.ts | 7 ++- src/api/types/chats.ts | 3 +- src/assets/localization/fallback.strings | 3 ++ .../common/helpers/renderTextWithEntities.tsx | 7 +++ src/components/left/ChatFolderModal.tsx | 18 +++++-- src/components/left/main/ChatFolders.tsx | 10 ++-- .../settings/folders/SettingsFoldersEdit.tsx | 2 +- .../settings/folders/SettingsFoldersMain.tsx | 28 ++++++++-- .../folders/SettingsShareChatlist.tsx | 54 +++++++++++-------- .../modals/chatlist/ChatlistModal.tsx | 21 ++++++-- src/components/ui/Tab.scss | 1 + src/components/ui/Tab.tsx | 6 +-- src/components/ui/TabList.tsx | 13 ++--- src/hooks/reducers/useFoldersReducer.ts | 8 +-- src/types/language.d.ts | 7 +++ src/util/localization/types.ts | 16 +++--- 17 files changed, 146 insertions(+), 68 deletions(-) diff --git a/src/api/gramjs/apiBuilders/chats.ts b/src/api/gramjs/apiBuilders/chats.ts index 5fd7f8064..998353735 100644 --- a/src/api/gramjs/apiBuilders/chats.ts +++ b/src/api/gramjs/apiBuilders/chats.ts @@ -26,7 +26,9 @@ import type { import { pick, pickTruthy } from '../../../util/iteratees'; import { getServerTime, getServerTimeOffset } from '../../../util/serverTime'; import { addPhotoToLocalDb, addUserToLocalDb, serializeBytes } from '../helpers'; -import { buildApiPhoto, buildApiUsernames, buildAvatarPhotoId } from './common'; +import { + buildApiFormattedText, buildApiPhoto, buildApiUsernames, buildAvatarPhotoId, +} from './common'; import { omitVirtualClassFields } from './helpers'; import { buildApiEmojiStatus, @@ -419,7 +421,8 @@ export function buildApiChatFolder(filter: GramJs.DialogFilter | GramJs.DialogFi pinnedChatIds: filter.pinnedPeers.map(getApiChatIdFromMtpPeer).filter(Boolean), hasMyInvites: filter.hasMyInvites, isChatList: true, - title: filter.title.text, + noTitleAnimations: filter.titleNoanimate, + title: buildApiFormattedText(filter.title), }; } @@ -432,7 +435,8 @@ export function buildApiChatFolder(filter: GramJs.DialogFilter | GramJs.DialogFi pinnedChatIds: filter.pinnedPeers.map(getApiChatIdFromMtpPeer).filter(Boolean), includedChatIds: filter.includePeers.map(getApiChatIdFromMtpPeer).filter(Boolean), excludedChatIds: filter.excludePeers.map(getApiChatIdFromMtpPeer).filter(Boolean), - title: filter.title.text, + title: buildApiFormattedText(filter.title), + noTitleAnimations: filter.titleNoanimate, }; } diff --git a/src/api/gramjs/gramjsBuilders/index.ts b/src/api/gramjs/gramjsBuilders/index.ts index 0de089204..71f9790fb 100644 --- a/src/api/gramjs/gramjsBuilders/index.ts +++ b/src/api/gramjs/gramjsBuilders/index.ts @@ -238,6 +238,7 @@ export function buildFilterFromApiFolder(folder: ApiChatFolder): GramJs.DialogFi pinnedChatIds, includedChatIds, excludedChatIds, + noTitleAnimations, } = folder; const pinnedPeers = pinnedChatIds @@ -255,17 +256,18 @@ export function buildFilterFromApiFolder(folder: ApiChatFolder): GramJs.DialogFi if (folder.isChatList) { return new GramJs.DialogFilterChatlist({ id: folder.id, - title: buildInputTextWithEntities({ text: folder.title, entities: [] }), + title: buildInputTextWithEntities(folder.title), emoticon: emoticon || undefined, pinnedPeers, includePeers, hasMyInvites: folder.hasMyInvites, + titleNoanimate: noTitleAnimations, }); } return new GramJs.DialogFilter({ id: folder.id, - title: buildInputTextWithEntities({ text: folder.title, entities: [] }), + title: buildInputTextWithEntities(folder.title), emoticon: emoticon || undefined, contacts: contacts || undefined, nonContacts: nonContacts || undefined, @@ -278,6 +280,7 @@ export function buildFilterFromApiFolder(folder: ApiChatFolder): GramJs.DialogFi pinnedPeers, includePeers, excludePeers, + titleNoanimate: noTitleAnimations, }); } diff --git a/src/api/types/chats.ts b/src/api/types/chats.ts index db24d5620..ca498ed45 100644 --- a/src/api/types/chats.ts +++ b/src/api/types/chats.ts @@ -207,7 +207,8 @@ export interface ApiRestrictionReason { export interface ApiChatFolder { id: number; - title: string; + title: ApiFormattedText; + noTitleAnimations?: true; description?: string; emoticon?: string; contacts?: true; diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index c31d37c8d..943a8c755 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -1406,3 +1406,6 @@ "StarsSubscribeBotText_one" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** star per month?" "StarsSubscribeBotText_other" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** stars per month?" "StarsSubscribeBotButtonMonth" = "Subscribe for {amount} / month"; +"FolderLinkTitleDescription" = "Anyone with this link can add {folder} folder and {chats} selected below."; +"FolderLinkTitleDescriptionChats_one" = "the chat"; +"FolderLinkTitleDescriptionChats_other" = "the {count} chats"; diff --git a/src/components/common/helpers/renderTextWithEntities.tsx b/src/components/common/helpers/renderTextWithEntities.tsx index a8239e55d..64295c17d 100644 --- a/src/components/common/helpers/renderTextWithEntities.tsx +++ b/src/components/common/helpers/renderTextWithEntities.tsx @@ -46,6 +46,7 @@ export function renderTextWithEntities({ sharedCanvasHqRef, cacheBuster, forcePlayback, + noCustomEmojiPlayback, focusedQuote, isInSelectMode, }: { @@ -65,6 +66,7 @@ export function renderTextWithEntities({ sharedCanvasHqRef?: React.RefObject; cacheBuster?: string; forcePlayback?: boolean; + noCustomEmojiPlayback?: boolean; focusedQuote?: string; isInSelectMode?: boolean; }) { @@ -173,6 +175,7 @@ export function renderTextWithEntities({ sharedCanvasHqRef, cacheBuster, forcePlayback, + noCustomEmojiPlayback, isInSelectMode, }); @@ -388,6 +391,7 @@ function processEntity({ sharedCanvasHqRef, cacheBuster, forcePlayback, + noCustomEmojiPlayback, isInSelectMode, } : { entity: ApiMessageEntity; @@ -407,6 +411,7 @@ function processEntity({ sharedCanvasHqRef?: React.RefObject; cacheBuster?: string; forcePlayback?: boolean; + noCustomEmojiPlayback?: boolean; isInSelectMode?: boolean; }) { const entityText = typeof entityContent === 'string' && entityContent; @@ -447,6 +452,7 @@ function processEntity({ observeIntersectionForPlaying={observeIntersectionForPlaying} withTranslucentThumb={withTranslucentThumbs} forceAlways={forcePlayback} + noPlay={noCustomEmojiPlayback} /> ); } @@ -581,6 +587,7 @@ function processEntity({ observeIntersectionForPlaying={observeIntersectionForPlaying} withTranslucentThumb={withTranslucentThumbs} forceAlways={forcePlayback} + noPlay={noCustomEmojiPlayback} /> ); default: diff --git a/src/components/left/ChatFolderModal.tsx b/src/components/left/ChatFolderModal.tsx index dd048a20f..ed87eef68 100644 --- a/src/components/left/ChatFolderModal.tsx +++ b/src/components/left/ChatFolderModal.tsx @@ -8,6 +8,7 @@ import type { ApiChatFolder } from '../../api/types'; import { ALL_FOLDER_ID } from '../../config'; import buildClassName from '../../util/buildClassName'; +import { renderTextWithEntities } from '../common/helpers/renderTextWithEntities'; import useOldLang from '../../hooks/useOldLang'; @@ -59,10 +60,19 @@ const ChatFolderModal: FC = ({ const [selectedFolderIds, setSelectedFolderIds] = useState(initialSelectedFolderIds); const folders = useMemo(() => { - return folderOrderedIds?.filter((folderId) => folderId !== ALL_FOLDER_ID).map((folderId) => ({ - label: foldersById ? foldersById[folderId].title : '', - value: String(folderId), - })) || []; + return folderOrderedIds?.filter((folderId) => folderId !== ALL_FOLDER_ID) + .map((folderId) => { + const folder = foldersById ? foldersById[folderId] : undefined; + const label = folder ? renderTextWithEntities({ + text: folder.title.text, + entities: folder.title.entities, + noCustomEmojiPlayback: folder.noTitleAnimations, + }) : ''; + return { + label, + value: String(folderId), + }; + }) || []; }, [folderOrderedIds, foldersById]); const handleSubmit = useCallback(() => { diff --git a/src/components/left/main/ChatFolders.tsx b/src/components/left/main/ChatFolders.tsx index f2788bf91..3501fb022 100644 --- a/src/components/left/main/ChatFolders.tsx +++ b/src/components/left/main/ChatFolders.tsx @@ -19,6 +19,7 @@ import captureEscKeyListener from '../../../util/captureEscKeyListener'; import { captureEvents, SwipeDirection } from '../../../util/captureEvents'; import { MEMO_EMPTY_ARRAY } from '../../../util/memo'; import { IS_TOUCH_ENV } from '../../../util/windowEnvironment'; +import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities'; import useDerivedState from '../../../hooks/useDerivedState'; import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager'; @@ -114,7 +115,7 @@ const ChatFolders: FC = ({ const allChatsFolder: ApiChatFolder = useMemo(() => { return { id: ALL_FOLDER_ID, - title: orderedFolderIds?.[0] === ALL_FOLDER_ID ? lang('FilterAllChatsShort') : lang('FilterAllChats'), + title: { text: orderedFolderIds?.[0] === ALL_FOLDER_ID ? lang('FilterAllChatsShort') : lang('FilterAllChats') }, includedChatIds: MEMO_EMPTY_ARRAY, excludedChatIds: MEMO_EMPTY_ARRAY, } satisfies ApiChatFolder; @@ -197,7 +198,11 @@ const ChatFolders: FC = ({ return { id, - title, + title: renderTextWithEntities({ + text: title.text, + entities: title.entities, + noCustomEmojiPlayback: folder.noTitleAnimations, + }), badgeCount: folderCountersById[id]?.chatsCount, isBadgeActive: Boolean(folderCountersById[id]?.notificationsCount), isBlocked, @@ -335,7 +340,6 @@ const ChatFolders: FC = ({ tabs={folderTabs} activeTab={activeChatFolder} onSwitchTab={handleSwitchTab} - areFolders /> ) : shouldRenderPlaceholder ? (
diff --git a/src/components/left/settings/folders/SettingsFoldersEdit.tsx b/src/components/left/settings/folders/SettingsFoldersEdit.tsx index 5e1bd3a57..ee9c1ba8e 100644 --- a/src/components/left/settings/folders/SettingsFoldersEdit.tsx +++ b/src/components/left/settings/folders/SettingsFoldersEdit.tsx @@ -298,7 +298,7 @@ const SettingsFoldersEdit: FC = ({ diff --git a/src/components/left/settings/folders/SettingsFoldersMain.tsx b/src/components/left/settings/folders/SettingsFoldersMain.tsx index ecb4f71c3..3e44a6798 100644 --- a/src/components/left/settings/folders/SettingsFoldersMain.tsx +++ b/src/components/left/settings/folders/SettingsFoldersMain.tsx @@ -14,7 +14,7 @@ import { isBetween } from '../../../../util/math'; import { MEMO_EMPTY_ARRAY } from '../../../../util/memo'; import { throttle } from '../../../../util/schedulers'; import { LOCAL_TGS_URLS } from '../../../common/helpers/animatedAssets'; -import renderText from '../../../common/helpers/renderText'; +import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities'; import { useFolderManagerForChatsCount } from '../../../../hooks/useFolderManager'; import useHistoryBack from '../../../../hooks/useHistoryBack'; @@ -133,7 +133,10 @@ const SettingsFoldersMain: FC = ({ if (id === ALL_FOLDER_ID) { return { id, - title: lang('FilterAllChats'), + title: { + text: lang('FilterAllChats'), + entities: [], + }, }; } @@ -142,6 +145,7 @@ const SettingsFoldersMain: FC = ({ title: folder.title, subtitle: getFolderDescriptionText(lang, folder, chatsCountByFolderId[folder.id]), isChatList: folder.isChatList, + noTitleAnimations: folder.noTitleAnimations, }; }); }, [folderIds, foldersById, lang, chatsCountByFolderId]); @@ -252,7 +256,11 @@ const SettingsFoldersMain: FC = ({ allowSelection > - {folder.title} + {renderTextWithEntities({ + text: folder.title.text, + entities: folder.title.entities, + noCustomEmojiPlayback: folder.noTitleAnimations, + })} {lang('FoldersAllChatsDesc')} @@ -297,7 +305,11 @@ const SettingsFoldersMain: FC = ({ }} > - {renderText(folder.title, ['emoji'])} + {renderTextWithEntities({ + text: folder.title.text, + entities: folder.title.entities, + noCustomEmojiPlayback: folder.noTitleAnimations, + })} {isBlocked && } @@ -330,7 +342,13 @@ const SettingsFoldersMain: FC = ({ >
- {renderText(folder.title, ['emoji'])} + + {renderTextWithEntities({ + text: folder.title.text, + entities: folder.title.entities, + noCustomEmojiPlayback: folder.noTitleAnimations, + })} + {folder.description}
diff --git a/src/components/left/settings/folders/SettingsShareChatlist.tsx b/src/components/left/settings/folders/SettingsShareChatlist.tsx index a1358c74a..a27274c77 100644 --- a/src/components/left/settings/folders/SettingsShareChatlist.tsx +++ b/src/components/left/settings/folders/SettingsShareChatlist.tsx @@ -4,6 +4,8 @@ import React, { } from '../../../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../../../global'; +import type { ApiChatFolder } from '../../../../api/types'; + import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config'; import { isChatChannel, isUserBot } from '../../../../global/helpers'; import { @@ -13,10 +15,11 @@ import { } from '../../../../global/selectors'; import { partition } from '../../../../util/iteratees'; import { LOCAL_TGS_URLS } from '../../../common/helpers/animatedAssets'; -import renderText from '../../../common/helpers/renderText'; +import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities'; import useEffectWithPrevDeps from '../../../../hooks/useEffectWithPrevDeps'; import useHistoryBack from '../../../../hooks/useHistoryBack'; +import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; import useOldLang from '../../../../hooks/useOldLang'; @@ -33,9 +36,7 @@ type OwnProps = { type StateProps = { folderId?: number; - title?: string; - includedChatIds?: string[]; - pinnedChatIds?: string[]; + folder?: ApiChatFolder; peerIds?: string[]; url?: string; isLoading?: boolean; @@ -45,9 +46,7 @@ const SettingsShareChatlist: FC = ({ isActive, onReset, folderId, - title, - includedChatIds, - pinnedChatIds, + folder, peerIds, url, isLoading, @@ -55,7 +54,9 @@ const SettingsShareChatlist: FC = ({ const { createChatlistInvite, deleteChatlistInvite, editChatlistInvite, showNotification, } = getActions(); - const lang = useOldLang(); + + const lang = useLang(); + const oldLang = useOldLang(); const [isTouched, setIsTouched] = useState(false); @@ -84,8 +85,8 @@ const SettingsShareChatlist: FC = ({ }); const itemIds = useMemo(() => { - return (includedChatIds || []).concat(pinnedChatIds || []); - }, [includedChatIds, pinnedChatIds]); + return (folder?.includedChatIds || []).concat(folder?.pinnedChatIds || []); + }, [folder?.includedChatIds, folder?.pinnedChatIds]); const [unlockedIds, lockedIds] = useMemo(() => { const global = getGlobal(); @@ -114,19 +115,19 @@ const SettingsShareChatlist: FC = ({ const chat = selectChat(global, id); if (user && isUserBot(user)) { showNotification({ - message: lang('FolderLinkScreen.AlertTextUnavailableBot'), + message: oldLang('FolderLinkScreen.AlertTextUnavailableBot'), }); } else if (user) { showNotification({ - message: lang('FolderLinkScreen.AlertTextUnavailableUser'), + message: oldLang('FolderLinkScreen.AlertTextUnavailableUser'), }); } else if (chat && isChatChannel(chat)) { showNotification({ - message: lang('FolderLinkScreen.AlertTextUnavailablePublicChannel'), + message: oldLang('FolderLinkScreen.AlertTextUnavailablePublicChannel'), }); } else { showNotification({ - message: lang('FolderLinkScreen.AlertTextUnavailablePublicGroup'), + message: oldLang('FolderLinkScreen.AlertTextUnavailablePublicGroup'), }); } }); @@ -153,15 +154,26 @@ const SettingsShareChatlist: FC = ({ className="settings-content-icon" /> -

- {renderText(lang('FolderLinkScreen.TitleDescriptionSelected', [title, chatsCount]), - ['simple_markdown'])} -

+ {folder && ( +

+ {lang('FolderLinkTitleDescription', { + folder: renderTextWithEntities({ + text: folder.title.text, + entities: folder.title.entities, + noCustomEmojiPlayback: folder.noTitleAnimations, + }), + chats: lang('FolderLinkTitleDescriptionChats', { count: chatsCount }, { pluralValue: chatsCount }), + }, { + withMarkdown: true, + withNodes: true, + })} +

+ )}
( return { folderId, - title: folder?.title, - includedChatIds: folder?.includedChatIds, - pinnedChatIds: folder?.pinnedChatIds, + folder, url, isLoading, peerIds: invite?.peerIds, diff --git a/src/components/modals/chatlist/ChatlistModal.tsx b/src/components/modals/chatlist/ChatlistModal.tsx index da0b30fe6..8fd7aa68b 100644 --- a/src/components/modals/chatlist/ChatlistModal.tsx +++ b/src/components/modals/chatlist/ChatlistModal.tsx @@ -1,4 +1,4 @@ -import type { FC } from '../../../lib/teact/teact'; +import type { FC, TeactNode } from '../../../lib/teact/teact'; import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; @@ -6,6 +6,7 @@ import type { ApiChatFolder } from '../../../api/types'; import type { TabState } from '../../../global/types'; import { selectChatFolder } from '../../../global/selectors'; +import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities'; import useOldLang from '../../../hooks/useOldLang'; import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated'; @@ -55,7 +56,13 @@ const ChatlistInviteModal: FC = ({ }, [lang, renderingInfo]); const renderingFolderTitle = useMemo(() => { - if (renderingFolder) return renderingFolder.title; + if (renderingFolder) { + return renderTextWithEntities({ + text: renderingFolder.title.text, + entities: renderingFolder.title.entities, + noCustomEmojiPlayback: renderingFolder.noTitleAnimations, + }); + } if (renderingInfo?.invite && 'title' in renderingInfo.invite) return renderingInfo.invite.title; return undefined; }, [renderingFolder, renderingInfo]); @@ -66,12 +73,18 @@ const ChatlistInviteModal: FC = ({ return undefined; }, [renderingInfo]); - function renderFolders(folderTitle: string) { + function renderFolders(folderTitle: TeactNode) { return (
- +
diff --git a/src/components/ui/Tab.scss b/src/components/ui/Tab.scss index 3c1825bfd..c1124efc5 100644 --- a/src/components/ui/Tab.scss +++ b/src/components/ui/Tab.scss @@ -49,6 +49,7 @@ display: flex; align-items: center; white-space: nowrap; + gap: 1px; // Prevent custom emoji sticking to the text } .badge { diff --git a/src/components/ui/Tab.tsx b/src/components/ui/Tab.tsx index 7ea854b76..9ba1dcfd9 100644 --- a/src/components/ui/Tab.tsx +++ b/src/components/ui/Tab.tsx @@ -1,4 +1,4 @@ -import type { FC } from '../../lib/teact/teact'; +import type { FC, TeactNode } from '../../lib/teact/teact'; import React, { useEffect, useLayoutEffect, useRef } from '../../lib/teact/teact'; import type { MenuItemContextAction } from './ListItem'; @@ -21,7 +21,7 @@ import './Tab.scss'; type OwnProps = { className?: string; - title: string; + title: TeactNode; isActive?: boolean; isBlocked?: boolean; badgeCount?: number; @@ -139,7 +139,7 @@ const Tab: FC = ({ ref={tabRef} > - {renderText(title)} + {typeof title === 'string' ? renderText(title) : title} {Boolean(badgeCount) && ( {badgeCount} )} diff --git a/src/components/ui/TabList.tsx b/src/components/ui/TabList.tsx index 95034decc..93faad5f0 100644 --- a/src/components/ui/TabList.tsx +++ b/src/components/ui/TabList.tsx @@ -1,9 +1,8 @@ -import type { FC } from '../../lib/teact/teact'; +import type { FC, TeactNode } from '../../lib/teact/teact'; import React, { memo, useEffect, useRef } from '../../lib/teact/teact'; import type { MenuItemContextAction } from './ListItem'; -import { ALL_FOLDER_ID } from '../../config'; import animateHorizontalScroll from '../../util/animateHorizontalScroll'; import buildClassName from '../../util/buildClassName'; import { IS_ANDROID, IS_IOS } from '../../util/windowEnvironment'; @@ -18,7 +17,7 @@ import './TabList.scss'; export type TabWithProperties = { id?: number; - title: string; + title: TeactNode; badgeCount?: number; isBlocked?: boolean; isBadgeActive?: boolean; @@ -27,7 +26,6 @@ export type TabWithProperties = { type OwnProps = { tabs: readonly TabWithProperties[]; - areFolders?: boolean; activeTab: number; className?: string; tabClassName?: string; @@ -40,7 +38,7 @@ const TAB_SCROLL_THRESHOLD_PX = 16; const SCROLL_DURATION = IS_IOS ? 450 : IS_ANDROID ? 400 : 300; const TabList: FC = ({ - tabs, areFolders, activeTab, onSwitchTab, + tabs, activeTab, onSwitchTab, contextRootElementSelector, className, tabClassName, }) => { // eslint-disable-next-line no-null/no-null @@ -83,9 +81,8 @@ const TabList: FC = ({ > {tabs.map((tab, i) => ( = ( state, action, -) => { +): FoldersState => { switch (action.type) { case 'setTitle': return { ...state, folder: { ...state.folder, - title: action.payload, + title: { text: action.payload }, }, isTouched: true, }; @@ -184,7 +184,7 @@ const foldersReducer: StateReducer = ( ...state, folder: { ...omit(state.folder, INCLUDE_FILTER_FIELDS), - title: state.folder.title ? state.folder.title : getSuggestedFolderName(state.includeFilters), + title: state.folder.title ? state.folder.title : { text: getSuggestedFolderName(state.includeFilters) }, ...state.includeFilters, }, includeFilters: undefined, diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 4a469f8f3..f0bf27989 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1574,6 +1574,10 @@ export interface LangPairWithVariables { 'StarsSubscribeBotButtonMonth': { 'amount': V; }; + 'FolderLinkTitleDescription': { + 'folder': V; + 'chats': V; + }; } export interface LangPairPlural { @@ -1755,6 +1759,9 @@ export interface LangPairPluralWithVariables { 'bot': V; 'amount': V; }; + 'FolderLinkTitleDescriptionChats': { + 'count': V; + }; } export type RegularLangKey = keyof LangPair; export type RegularLangKeyWithVariables = keyof LangPairWithVariables; diff --git a/src/util/localization/types.ts b/src/util/localization/types.ts index 039ad43b3..6d74bfc2e 100644 --- a/src/util/localization/types.ts +++ b/src/util/localization/types.ts @@ -122,11 +122,11 @@ export type LangFn = { ( key: K, variables: undefined, options: LangFnOptionsWithPlural, ): string; - ( - key: K, variables: V, options?: LangFnOptions, + ( + key: K, variables: LangPairWithVariables[K], options?: LangFnOptions, ): string; - ( - key: K, variables: V, options: LangFnOptionsWithPlural, + ( + key: K, variables: LangPairPluralWithVariables[K], options: LangFnOptionsWithPlural, ): string; ( @@ -135,11 +135,11 @@ export type LangFn = { ( key: K, variables: undefined, options: AdvancedLangFnOptionsWithPlural, ): TeactNode; - ( - key: K, variables: V, options: AdvancedLangFnOptions, + ( + key: K, variables: LangPairWithVariables[K], options: AdvancedLangFnOptions, ): TeactNode; - ( - key: K, variables: V, options: AdvancedLangFnOptionsWithPlural, + ( + key: K, variables: LangPairPluralWithVariables[K], options: AdvancedLangFnOptionsWithPlural, ): TeactNode; with: (params: LangFnParameters) => TeactNode;