diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 5dd1ab7a0..fa8a8af93 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -84,6 +84,12 @@ declare module '*.jpg' { const url: string; export default url; } + +declare module '*.webp' { + const url: string; + export default url; +} + declare module '*.svg' { const url: string; export default url; diff --git a/src/assets/cocoon.webp b/src/assets/cocoon.webp new file mode 100644 index 000000000..100c20ade Binary files /dev/null and b/src/assets/cocoon.webp differ diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index 29745f142..cb509c7d6 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -2608,6 +2608,23 @@ "SettingsDataClearMediaCache" = "Clear Media Cache"; "SettingsDataClearMediaCacheDescription" = "Deletes locally cached media for this account"; "SettingsDataClearMediaDone" = "Media cache cleared"; +"TranslateMenuCocoon" = "Translations are powered by 🥚 **Cocoon**. {link}"; +"TranslateMenuCocoonLinkText" = "How does it work?"; +"CocoonTitle" = "Cocoon"; +"CocoonDescription" = "Cocoon (**Co**nfidential **Co**mpute **O**pen **N**etwork)\nhandles AI tasks safely and efficiently."; +"CocoonFeature1Title" = "Private"; +"CocoonFeature1Text" = "No third party can access any data, such as translations, inside **{username}**."; +"CocoonFeature1Username" = "@cocoon"; +"CocoonFeature1UsernameLink" = "https://t.me/cocoon"; +"CocoonFeature2Title" = "Efficient"; +"CocoonFeature2Text" = "Cocoon has allowed Telegram to reduce translation cost by 6x."; +"CocoonFeature3Title" = "For Everyone"; +"CocoonFeature3Text" = "Any developer can use Cocoon for AI features. Learn more at **{link}**."; +"CocoonFeature3LinkText" = "cocoon.org"; +"CocoonFeature3Link" = "https://cocoon.org"; +"CocoonFooterText" = "Want to integrate Cocoon into your projects?\nReach out **{link}**"; +"CocoonFooterLinkText" = "directly to @cocoon"; +"CocoonFooterLink" = "https://t.me/cocoon?direct"; "ChatListAuctionTitle_one" = "Active Auction"; "ChatListAuctionTitle_other" = "{count} Active Auctions"; "ChatListAuctionWinning" = "You are winning"; diff --git a/src/bundles/extra.ts b/src/bundles/extra.ts index 7bf25ae2f..05e91a88e 100644 --- a/src/bundles/extra.ts +++ b/src/bundles/extra.ts @@ -108,3 +108,4 @@ export { default as ProfileRatingModal } from '../components/modals/profileRatin export { default as QuickPreviewModal } from '../components/modals/quickPreview/QuickPreviewModal'; export { default as StealthModeModal } from '../components/modals/storyStealthMode/StealthModeModal'; export { default as QuickChatPickerModal } from '../components/modals/quickChatPicker/QuickChatPickerModal'; +export { default as CocoonModal } from '../components/modals/cocoon/CocoonModal'; diff --git a/src/components/common/InteractiveSparkles.tsx b/src/components/common/InteractiveSparkles.tsx index fa5863117..41e9ad0a7 100644 --- a/src/components/common/InteractiveSparkles.tsx +++ b/src/components/common/InteractiveSparkles.tsx @@ -17,6 +17,8 @@ const DEFAULT_PARTICLE_PARAMS = { centerShift: [0, -36] as const, }; +const MIN_BURST_INTERVAL = 8; + const InteractiveSparkles = ({ color = 'purple', centerShift = DEFAULT_PARTICLE_PARAMS.centerShift, @@ -25,6 +27,7 @@ const InteractiveSparkles = ({ onRequestAnimation, }: OwnProps) => { const canvasRef = useRef(); + const lastBurstTimeRef = useRef(0); useLayoutEffect(() => { if (isDisabled) return undefined; @@ -41,6 +44,11 @@ const InteractiveSparkles = ({ const animate = () => { if (isDisabled) return; + const now = Date.now(); + if (now - lastBurstTimeRef.current < MIN_BURST_INTERVAL) return; + + lastBurstTimeRef.current = now; + setupParticles(canvasRef.current!, { color: PARTICLE_COLORS[`${color}Gradient`], centerShift, diff --git a/src/components/middle/HeaderActions.tsx b/src/components/middle/HeaderActions.tsx index bb5974842..6a27a3f6e 100644 --- a/src/components/middle/HeaderActions.tsx +++ b/src/components/middle/HeaderActions.tsx @@ -8,6 +8,7 @@ import type { IAnchorPosition, MessageListType, ThreadId } from '../../types'; import { MAIN_THREAD_ID } from '../../api/types'; import { ManagementScreens } from '../../types'; +import { COCOON_EMOJI_ID } from '../../config'; import { requestMeasure, requestNextMutation } from '../../lib/fasterdom/fasterdom'; import { getHasAdminRight, @@ -38,11 +39,14 @@ import { isUserId } from '../../util/entities/ids'; import focusNoScroll from '../../util/focusNoScroll'; import { useHotkeys } from '../../hooks/useHotkeys'; +import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; +import CustomEmoji from '../common/CustomEmoji'; import Button from '../ui/Button'; import DropdownMenu from '../ui/DropdownMenu'; +import Link from '../ui/Link'; import MenuItem from '../ui/MenuItem'; import MenuSeparator from '../ui/MenuSeparator'; import HeaderMenuContainer from './HeaderMenuContainer.async'; @@ -142,9 +146,12 @@ const HeaderActions: FC = ({ unblockUser, setViewForumAsMessages, openFrozenAccountModal, + openCocoonModal, } = getActions(); const menuButtonRef = useRef(); - const lang = useOldLang(); + const oldLang = useOldLang(); + const lang = useLang(); + const [isMenuOpen, setIsMenuOpen] = useState(false); const [menuAnchor, setMenuAnchor] = useState(undefined); @@ -166,7 +173,8 @@ const HeaderActions: FC = ({ joinChannel({ chatId }); if (shouldSendJoinRequest) { showNotification({ - message: isChannel ? lang('RequestToJoinChannelSentDescription') : lang('RequestToJoinGroupSentDescription'), + message: isChannel ? oldLang('RequestToJoinChannelSentDescription') + : oldLang('RequestToJoinGroupSentDescription'), }); } }); @@ -239,21 +247,21 @@ const HeaderActions: FC = ({ const getTextWithLanguage = useCallback((langKey: string, langCode: string) => { const simplified = langCode.split('-')[0]; const translationKey = `TranslateLanguage${simplified.toUpperCase()}`; - const name = lang(translationKey); + const name = oldLang(translationKey); if (name !== translationKey) { - return lang(langKey, name); + return oldLang(langKey, name); } const translatedNames = new Intl.DisplayNames([language], { type: 'language' }); const translatedName = translatedNames.of(langCode)!; - return lang(`${langKey}Other`, translatedName); - }, [language, lang]); + return oldLang(`${langKey}Other`, translatedName); + }, [language, oldLang]); const buttonText = useMemo(() => { - if (isTranslating) return lang('ShowOriginalButton'); + if (isTranslating) return oldLang('ShowOriginalButton'); return getTextWithLanguage('TranslateToButton', translationLanguage); - }, [translationLanguage, getTextWithLanguage, isTranslating, lang]); + }, [translationLanguage, getTextWithLanguage, isTranslating, oldLang]); const doNotTranslateText = useMemo(() => { if (!detectedChatLanguage) return undefined; @@ -270,6 +278,10 @@ const HeaderActions: FC = ({ openChatLanguageModal({ chatId }); }); + const handleCocoonClick = useLastCallback(() => { + openCocoonModal(); + }); + const handleDoNotTranslate = useLastCallback(() => { if (!detectedChatLanguage) return; @@ -294,11 +306,11 @@ const HeaderActions: FC = ({ size="smaller" className={isOpen ? 'active' : ''} onClick={onTrigger} - ariaLabel={lang('TranslateMessage')} + ariaLabel={oldLang('TranslateMessage')} iconName="language" /> ); - }, [isRightColumnShown, lang]); + }, [isRightColumnShown, oldLang]); return (
@@ -312,12 +324,28 @@ const HeaderActions: FC = ({ {buttonText} - {lang('Chat.Translate.Menu.To')} + {oldLang('Chat.Translate.Menu.To')} {detectedChatLanguage && {doNotTranslateText}} - {lang('Hide')} + {oldLang('Hide')} + + + {lang('TranslateMenuCocoon', { + link: ( + e.preventDefault()}> + {lang('TranslateMenuCocoonLinkText')} + + ), + }, { + withNodes: true, + withMarkdown: true, + specialReplacement: { + '🥚': , + }, + })} + )} {!isMobile && ( @@ -329,7 +357,7 @@ const HeaderActions: FC = ({ fluid onClick={handleSubscribeClick} > - {lang(isChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')} + {oldLang(isChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')} )} {canExpandActions && shouldSendJoinRequest && ( @@ -339,7 +367,7 @@ const HeaderActions: FC = ({ fluid onClick={handleSubscribeClick} > - {lang('ChannelJoinRequest')} + {oldLang('ChannelJoinRequest')} )} {canExpandActions && canStartBot && ( @@ -349,7 +377,7 @@ const HeaderActions: FC = ({ fluid onClick={handleStartBot} > - {lang('BotStart')} + {oldLang('BotStart')} )} {canExpandActions && canRestartBot && ( @@ -359,7 +387,7 @@ const HeaderActions: FC = ({ fluid onClick={handleRestartBot} > - {lang('BotRestart')} + {oldLang('BotRestart')} )} {canExpandActions && canUnblock && ( @@ -369,7 +397,7 @@ const HeaderActions: FC = ({ fluid onClick={handleUnblock} > - {lang('Unblock')} + {oldLang('Unblock')} )} {canSearch && ( @@ -379,7 +407,7 @@ const HeaderActions: FC = ({ color="translucent" size="smaller" onClick={handleSearchClick} - ariaLabel={lang('Conversation.SearchPlaceholder')} + ariaLabel={oldLang('Conversation.SearchPlaceholder')} iconName="search" /> )} @@ -404,7 +432,7 @@ const HeaderActions: FC = ({ size="smaller" iconName="user" onClick={handleJoinRequestsClick} - ariaLabel={isChannel ? lang('SubscribeRequests') : lang('MemberRequests')} + ariaLabel={isChannel ? oldLang('SubscribeRequests') : oldLang('MemberRequests')} >
{pendingJoinRequests}
diff --git a/src/components/middle/message/_message-content.scss b/src/components/middle/message/_message-content.scss index cc289c437..baca3d26f 100644 --- a/src/components/middle/message/_message-content.scss +++ b/src/components/middle/message/_message-content.scss @@ -583,13 +583,13 @@ &:not(.custom-shape) .text-content { .emoji { - width: calc(1.25 * var(--message-text-size, 1rem)); - height: calc(1.25 * var(--message-text-size, 1rem)); - background-size: calc(1.25 * var(--message-text-size, 1rem)); + width: 1.25em; + height: 1.25em; + background-size: 1.25em; } .custom-emoji { - --custom-emoji-size: max(calc(1.25 * var(--message-text-size, 1rem)), 20px); + --custom-emoji-size: max(1.25em, 20px); width: var(--custom-emoji-size); height: var(--custom-emoji-size); diff --git a/src/components/modals/ModalContainer.tsx b/src/components/modals/ModalContainer.tsx index c3d68981b..2c784e4b8 100644 --- a/src/components/modals/ModalContainer.tsx +++ b/src/components/modals/ModalContainer.tsx @@ -15,6 +15,7 @@ import BirthdaySetupModal from './birthday/BirthdaySetupModal.async'; import BoostModal from './boost/BoostModal.async'; import ChatInviteModal from './chatInvite/ChatInviteModal.async'; import ChatlistModal from './chatlist/ChatlistModal.async'; +import CocoonModal from './cocoon/CocoonModal.async'; import CollectibleInfoModal from './collectible/CollectibleInfoModal.async'; import DeleteAccountModal from './deleteAccount/DeleteAccountModal.async'; import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async'; @@ -130,7 +131,8 @@ type ModalKey = keyof Pick; type StateProps = { @@ -207,6 +209,7 @@ const MODALS: ModalRegistry = { isPasskeyModalOpen: PasskeyModal, birthdaySetupModal: BirthdaySetupModal, isQuickChatPickerOpen: QuickChatPickerModal, + isCocoonModalOpen: CocoonModal, }; const MODAL_KEYS = Object.keys(MODALS) as ModalKey[]; const MODAL_ENTRIES = Object.entries(MODALS) as Entries; diff --git a/src/components/modals/cocoon/CocoonModal.async.tsx b/src/components/modals/cocoon/CocoonModal.async.tsx new file mode 100644 index 000000000..a959c720b --- /dev/null +++ b/src/components/modals/cocoon/CocoonModal.async.tsx @@ -0,0 +1,14 @@ +import type { OwnProps } from './CocoonModal'; + +import { Bundles } from '../../../util/moduleLoader'; + +import useModuleLoader from '../../../hooks/useModuleLoader'; + +const CocoonModalAsync = (props: OwnProps) => { + const { modal } = props; + const CocoonModal = useModuleLoader(Bundles.Extra, 'CocoonModal', !modal); + + return CocoonModal ? : undefined; +}; + +export default CocoonModalAsync; diff --git a/src/components/modals/cocoon/CocoonModal.module.scss b/src/components/modals/cocoon/CocoonModal.module.scss new file mode 100644 index 000000000..907caed32 --- /dev/null +++ b/src/components/modals/cocoon/CocoonModal.module.scss @@ -0,0 +1,26 @@ +.header { + position: relative; + margin-top: -1rem; + margin-inline: -1rem; +} + +.footer { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.footerText { + font-size: 0.9375rem; + color: var(--color-text-secondary); + text-align: center; + text-wrap: balance; +} + +.egg { + transition: transform 0.25s ease-out; + + &:hover { + transform: scale(1.1); + } +} diff --git a/src/components/modals/cocoon/CocoonModal.tsx b/src/components/modals/cocoon/CocoonModal.tsx new file mode 100644 index 000000000..8014c90eb --- /dev/null +++ b/src/components/modals/cocoon/CocoonModal.tsx @@ -0,0 +1,108 @@ +import { memo, useMemo } from '@teact'; +import { getActions } from '../../../global'; + +import type { TabState } from '../../../global/types'; + +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; + +import SafeLink from '../../common/SafeLink'; +import Button from '../../ui/Button'; +import Link from '../../ui/Link'; +import ParticlesHeader from '../common/ParticlesHeader'; +import TableAboutModal, { type TableAboutData } from '../common/TableAboutModal'; + +import styles from './CocoonModal.module.scss'; + +export type OwnProps = { + modal: TabState['isCocoonModalOpen']; +}; + +const CocoonModal = ({ modal }: OwnProps) => { + const { closeCocoonModal, openTelegramLink } = getActions(); + const isOpen = Boolean(modal); + + const lang = useLang(); + + const handleClose = useLastCallback(() => { + closeCocoonModal(); + }); + + const openLinkAndClose = useLastCallback((url: string) => { + openTelegramLink({ url }); + handleClose(); + }); + + const listItemData = useMemo(() => { + const feature1Description = lang('CocoonFeature1Text', { + username: ( + { + openLinkAndClose(lang('CocoonFeature1UsernameLink')); + }} + > + {lang('CocoonFeature1Username')} + + ), + }, { withNodes: true, withMarkdown: true }); + + const feature3Description = lang('CocoonFeature3Text', { + link: , + }, { + withNodes: true, + withMarkdown: true, + }); + + return [ + ['lock', lang('CocoonFeature1Title'), feature1Description], + ['stats', lang('CocoonFeature2Title'), lang('CocoonFeature2Text')], + ['gift', lang('CocoonFeature3Title'), feature3Description], + ] satisfies TableAboutData; + }, [lang]); + + const header = useMemo(() => { + return ( + + ); + }, [lang]); + + const footer = useMemo(() => { + return ( +
+ + {lang('CocoonFooterText', { + link: ( + openLinkAndClose(lang('CocoonFooterLink'))}> + {lang('CocoonFooterLinkText')} + + ), + }, { withNodes: true, withMarkdown: true })} + + +
+ ); + }, [lang]); + + return ( + + ); +}; + +export default memo(CocoonModal); diff --git a/src/components/modals/common/ParticlesHeader.module.scss b/src/components/modals/common/ParticlesHeader.module.scss index 3b940028c..a13343ad8 100644 --- a/src/components/modals/common/ParticlesHeader.module.scss +++ b/src/components/modals/common/ParticlesHeader.module.scss @@ -5,6 +5,29 @@ padding-top: 9rem; } +.ai-egg { + padding-top: 10rem; + background-color: #061029; + background-image: radial-gradient(circle at center, #FFFFFF08 0%, #00000000 50%); + + .title { + margin-bottom: 0; + + font-size: 2.5rem; + color: transparent; + + background-image: linear-gradient(90deg, #67c9ff 0%, #cb56ff 100%); + background-clip: text; + } + + .description { + color: #b8c9ef; + :global(b) { + color: white; + } + } +} + .particles { position: absolute; top: 0; @@ -41,3 +64,10 @@ transform: scale(1.1); } } + +.cocoon { + position: absolute; + z-index: 1; + top: 1.75rem; + height: 8.125rem; +} diff --git a/src/components/modals/common/ParticlesHeader.tsx b/src/components/modals/common/ParticlesHeader.tsx index e8b683e53..525d7d13c 100644 --- a/src/components/modals/common/ParticlesHeader.tsx +++ b/src/components/modals/common/ParticlesHeader.tsx @@ -15,14 +15,17 @@ import SwayingStar from './SwayingStar.tsx'; import styles from './ParticlesHeader.module.scss'; +import Cocoon from '../../../assets/cocoon.webp'; + interface OwnProps { - model: 'swaying-star' | 'speeding-diamond' | 'sticker'; + model: 'swaying-star' | 'speeding-diamond' | 'ai-egg' | 'sticker'; sticker?: ApiSticker; color: 'purple' | 'gold' | 'blue'; title: TeactNode; description: TeactNode; isDisabled?: boolean; className?: string; + modelClassName?: string; } const GIFT_STICKER_SIZE = 8 * REM; @@ -39,6 +42,7 @@ function ParticlesHeader({ description, isDisabled, className, + modelClassName, }: OwnProps) { const stickerRef = useRef(); const triggerSparklesRef = useRef<(() => void) | undefined>(); @@ -52,7 +56,7 @@ function ParticlesHeader({ }); return ( -
+
+ ) : model === 'ai-egg' ? ( + ) : model === 'speeding-diamond' ? ( - + ) : model === 'sticker' && sticker && (
diff --git a/src/components/modals/common/SpeedingDiamond.tsx b/src/components/modals/common/SpeedingDiamond.tsx index 16f951310..a2eaf4642 100644 --- a/src/components/modals/common/SpeedingDiamond.tsx +++ b/src/components/modals/common/SpeedingDiamond.tsx @@ -2,6 +2,7 @@ import { memo, useState } from '@teact'; import { requestMutation } from '../../../lib/fasterdom/fasterdom.ts'; import { animateSingle } from '../../../util/animation.ts'; +import buildClassName from '../../../util/buildClassName'; import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets.ts'; import useLastCallback from '../../../hooks/useLastCallback.ts'; @@ -13,6 +14,7 @@ import styles from './SpeedingDiamond.module.scss'; import diamondPreviewUrl from '../../../assets/diamond.png'; interface OwnProps { + className?: string; onMouseMove: NoneToVoidFunction; } @@ -24,7 +26,7 @@ const SLOWDOWN_DURATION = 1500; let slowdownTimeout: number | undefined; let isAnimating = true; -function SpeedingDiamond({ onMouseMove }: OwnProps) { +function SpeedingDiamond({ className, onMouseMove }: OwnProps) { const [speed, setSpeed] = useState(MIN_SPEED); const handleMouseMove = useLastCallback(() => { @@ -58,7 +60,7 @@ function SpeedingDiamond({ onMouseMove }: OwnProps) { }); return ( -
+
diff --git a/src/components/modals/common/TableAboutModal.module.scss b/src/components/modals/common/TableAboutModal.module.scss index 103fcbbc6..a5cd55935 100644 --- a/src/components/modals/common/TableAboutModal.module.scss +++ b/src/components/modals/common/TableAboutModal.module.scss @@ -75,8 +75,10 @@ align-items: center; } -.listItem { - padding-bottom: 0.5rem; +.listItems { + display: flex; + flex-direction: column; + gap: 0.5rem; } .listItemTitle { @@ -86,5 +88,5 @@ .separator { width: calc(100% + 2rem); // Hack to cover modal paddings - margin-block: 1rem; + margin-bottom: 0.5rem; } diff --git a/src/components/modals/common/TableAboutModal.tsx b/src/components/modals/common/TableAboutModal.tsx index 65567c6c8..16ad61b4a 100644 --- a/src/components/modals/common/TableAboutModal.tsx +++ b/src/components/modals/common/TableAboutModal.tsx @@ -63,7 +63,7 @@ const TableAboutModal = ({
)} {header} -
+
{listItemData?.map(([icon, title, subtitle]) => { return ( ) => void; }; -const Link: FC = ({ +const Link = ({ children, isPrimary, className, isRtl, withMultilineFix, onClick, -}) => { +}: OwnProps) => { const handleClick = useLastCallback((e: React.MouseEvent) => { e.preventDefault(); onClick!(e); diff --git a/src/components/ui/MenuItem.scss b/src/components/ui/MenuItem.scss index f4e5fc284..f09b01d4d 100644 --- a/src/components/ui/MenuItem.scss +++ b/src/components/ui/MenuItem.scss @@ -161,11 +161,20 @@ font-weight: var(--font-weight-semibold); } + a:hover { + text-decoration: none; + } + &.wrap { display: block; white-space: normal; } + &.text-only { + padding-inline: 0.5rem; + line-height: 1.375; + } + &.menu-custom-emoji-sets { margin: 0 0.25rem; padding: 0.5rem 0.75rem; diff --git a/src/components/ui/MenuItem.tsx b/src/components/ui/MenuItem.tsx index ea323171c..eee9a59d0 100644 --- a/src/components/ui/MenuItem.tsx +++ b/src/components/ui/MenuItem.tsx @@ -91,6 +91,7 @@ const MenuItem = (props: MenuItemProps) => { destructive && 'destructive', !isTouchScreen && 'compact', withWrap && 'wrap', + !icon && !customIcon && 'text-only', ); const content = ( diff --git a/src/config.ts b/src/config.ts index 5aa0cf844..811f0bea7 100644 --- a/src/config.ts +++ b/src/config.ts @@ -345,6 +345,7 @@ export const REPLIES_USER_ID = '1271266957'; // TODO For Test connection ID must export const VERIFICATION_CODES_USER_ID = '489000'; export const ANONYMOUS_USER_ID = '2666000'; export const RESTRICTED_EMOJI_SET_ID = '7173162320003080'; +export const COCOON_EMOJI_ID = '5197252827247841976'; export const LOCAL_MESSAGES_LIMIT = 1e6; // 1M export const CHANNEL_ID_BASE = 10n ** 12n; export const DEFAULT_GIF_SEARCH_BOT_USERNAME = 'gif'; diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index c9d5ca961..459426d43 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -961,3 +961,12 @@ addActionHandler('openQuickChatPicker', (global, actions, payload): ActionReturn }); addTabStateResetterAction('closeQuickChatPicker', 'isQuickChatPickerOpen'); + +addActionHandler('openCocoonModal', (global, actions, payload): ActionReturnType => { + const { tabId = getCurrentTabId() } = payload || {}; + return updateTabState(global, { + isCocoonModalOpen: true, + }, tabId); +}); + +addTabStateResetterAction('closeCocoonModal', 'isCocoonModalOpen'); diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index 45def6392..c1c76364f 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -2996,6 +2996,9 @@ export interface ActionPayloads { chatId: string; messageId: number; }; + + openCocoonModal: WithTabId | undefined; + closeCocoonModal: WithTabId | undefined; } export interface RequiredActionPayloads { diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts index c0bc50547..ca74ef599 100644 --- a/src/global/types/tabState.ts +++ b/src/global/types/tabState.ts @@ -975,4 +975,6 @@ export type TabState = { isWaitingForStarGiftTransfer?: true; insertingPeerIdMention?: string; shouldSaveAttachmentsCompression?: boolean; + + isCocoonModalOpen?: boolean; }; diff --git a/src/lib/gramjs/tl/apiTl.ts b/src/lib/gramjs/tl/apiTl.ts index dcbebb4be..15e749cd7 100644 --- a/src/lib/gramjs/tl/apiTl.ts +++ b/src/lib/gramjs/tl/apiTl.ts @@ -1872,6 +1872,7 @@ payments.getStarsStatus#4ea9b3bf flags:# ton:flags.0?true peer:InputPeer = payme payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true ton:flags.4?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus; payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult; payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates; +payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true ton:flags.1?true peer:InputPeer = payments.StarsRevenueStats; payments.getStarsTransactionsByID#2dca16b8 flags:# ton:flags.0?true peer:InputPeer id:Vector = payments.StarsStatus; payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector; payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus; diff --git a/src/lib/gramjs/tl/static/api.json b/src/lib/gramjs/tl/static/api.json index b707c67eb..d598c6599 100644 --- a/src/lib/gramjs/tl/static/api.json +++ b/src/lib/gramjs/tl/static/api.json @@ -331,6 +331,7 @@ "payments.getStarsTransactions", "payments.getStarsTransactionsByID", "payments.getStarsSubscriptions", + "payments.getStarsRevenueStats", "payments.changeStarsSubscription", "payments.fulfillStarsSubscription", "payments.sendStarsForm", diff --git a/src/types/language.d.ts b/src/types/language.d.ts index d4bf564ab..7a51922b2 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1916,6 +1916,19 @@ export interface LangPair { 'SettingsDataClearMediaCache': undefined; 'SettingsDataClearMediaCacheDescription': undefined; 'SettingsDataClearMediaDone': undefined; + 'TranslateMenuCocoonLinkText': undefined; + 'CocoonTitle': undefined; + 'CocoonDescription': undefined; + 'CocoonFeature1Title': undefined; + 'CocoonFeature1Username': undefined; + 'CocoonFeature1UsernameLink': undefined; + 'CocoonFeature2Title': undefined; + 'CocoonFeature2Text': undefined; + 'CocoonFeature3Title': undefined; + 'CocoonFeature3LinkText': undefined; + 'CocoonFeature3Link': undefined; + 'CocoonFooterLinkText': undefined; + 'CocoonFooterLink': undefined; 'ChatListAuctionWinning': undefined; 'ChatListAuctionOutbid': undefined; 'ChatListAuctionView': undefined; @@ -3360,6 +3373,18 @@ export interface LangPairWithVariables { 'status': V; 'onlineCount': V; }; + 'TranslateMenuCocoon': { + 'link': V; + }; + 'CocoonFeature1Text': { + 'username': V; + }; + 'CocoonFeature3Text': { + 'link': V; + }; + 'CocoonFooterText': { + 'link': V; + }; 'ChatListAuctionMixed': { 'winCount': V; 'outbidCount': V; diff --git a/src/util/stopEvent.ts b/src/util/stopEvent.ts index 65e03f0fb..801591078 100644 --- a/src/util/stopEvent.ts +++ b/src/util/stopEvent.ts @@ -1,5 +1,3 @@ -import type React from '../lib/teact/teact'; - const stopEvent = (e: React.UIEvent | Event | React.FormEvent) => { e.stopPropagation(); e.preventDefault(); diff --git a/webpack.config.ts b/webpack.config.ts index 3f2580cba..873109e6a 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -164,7 +164,7 @@ export default function createConfig( ], }, { - test: /\.(woff(2)?|ttf|eot|svg|png|jpg|tgs)(\?v=\d+\.\d+\.\d+)?$/, + test: /\.(woff(2)?|ttf|eot|svg|png|jpg|tgs|webp)(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource', }, {