diff --git a/eslint.config.mjs b/eslint.config.mjs index a36c775f5..b79b79063 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -29,13 +29,14 @@ export default tseslint.config( quoteProps: 'as-needed', }), globalIgnores([ - 'src/lib/rlottie/rlottie-wasm.js', + 'src/lib/rlottie/**', 'src/lib/video-preview/polyfill', - 'src/lib/fasttextweb/fasttext-wasm.cjs', + 'src/lib/fasttextweb/**', 'src/lib/gramjs/tl/', - 'src/lib/lovely-chart', + 'src/lib/lovely-chart/**', 'src/lib/music-metadata-browser', 'src/lib/secret-sauce/', + 'src/lib/fastBlur.js', 'src/types/language.d.ts', 'dist/', 'dist-electron/', @@ -56,6 +57,7 @@ export default tseslint.config( 'no-template-curly-in-string': 'error', 'object-shorthand': 'error', curly: ['error', 'multi-line'], + eqeqeq: ['error', 'always'], 'no-implicit-coercion': [ 'error', { diff --git a/src/assets/font-icons/gift-transfer-inline.svg b/src/assets/font-icons/gift-transfer-inline.svg new file mode 100644 index 000000000..f3f0ca846 --- /dev/null +++ b/src/assets/font-icons/gift-transfer-inline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index 96911a9dd..0bf059bf3 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -1469,7 +1469,7 @@ "GiftInfoPeerDescriptionOutConverted_one" = "{peer} converted this gift to **{amount}** Star."; "GiftInfoPeerDescriptionOutConverted_other" = "{peer} converted this gift to **{amount}** Stars."; "GiftInfoDescriptionFreeUpgrade" = "Upgrade this gift for free to turn it to a unique collectible."; -"GiftInfoDescriptionUpgrade" = "Upgrade this gift to turn it to a unique collectible."; +"GiftInfoDescriptionUpgrade2" = "Upgrade this gift to turn it to a unique collectible."; "GiftInfoPeerDescriptionFreeUpgradeOut" = "{peer} can turn this gift to a unique collectible"; "GiftInfoDescriptionUpgraded" = "This gift was turned into a unique collectible."; "GiftInfoFrom" = "From"; diff --git a/src/components/common/gift/GiftMenuItems.tsx b/src/components/common/gift/GiftMenuItems.tsx index 24a4ac20d..46b8081c6 100644 --- a/src/components/common/gift/GiftMenuItems.tsx +++ b/src/components/common/gift/GiftMenuItems.tsx @@ -6,6 +6,7 @@ import type { } from '../../../api/types'; import { DEFAULT_STATUS_ICON_ID, TME_LINK_PREFIX } from '../../../config'; +import { STARS_CURRENCY_CODE } from '../../../config'; import { copyTextToClipboard } from '../../../util/clipboard'; import { formatDateAtTime } from '../../../util/dates/dateFormat'; import { getServerTime } from '../../../util/serverTime'; @@ -125,7 +126,7 @@ const GiftMenuItems = ({ if (!savedGift || savedGift.gift.type !== 'starGiftUnique' || !savedGift.inputGift) return; closeGiftInfoModal(); updateStarGiftPrice({ gift: savedGift.inputGift, price: { - currency: 'XTR', amount: 0, nanos: 0, + currency: STARS_CURRENCY_CODE, amount: 0, nanos: 0, } }); showNotification({ icon: 'unlist-outline', diff --git a/src/components/modals/common/TableInfoModal.tsx b/src/components/modals/common/TableInfoModal.tsx index 78496807a..969038b03 100644 --- a/src/components/modals/common/TableInfoModal.tsx +++ b/src/components/modals/common/TableInfoModal.tsx @@ -29,6 +29,7 @@ type OwnProps = { footer?: TeactNode; buttonText?: string; className?: string; + contentClassName?: string; hasBackdrop?: boolean; onClose: NoneToVoidFunction; onButtonClick?: NoneToVoidFunction; @@ -47,6 +48,7 @@ const TableInfoModal = ({ footer, buttonText, className, + contentClassName, hasBackdrop, onClose, onButtonClick, @@ -70,7 +72,7 @@ const TableInfoModal = ({ header={modalHeader} title={title} className={className} - contentClassName={styles.content} + contentClassName={buildClassName(styles.content, contentClassName)} onClose={onClose} withBalanceBar={withBalanceBar} currencyInBalanceBar={currencyInBalanceBar} diff --git a/src/components/modals/gift/UniqueGiftHeader.module.scss b/src/components/modals/gift/UniqueGiftHeader.module.scss index 72dd6d4e3..f09db17df 100644 --- a/src/components/modals/gift/UniqueGiftHeader.module.scss +++ b/src/components/modals/gift/UniqueGiftHeader.module.scss @@ -9,6 +9,24 @@ height: var(--_height); margin-bottom: 0.5rem; padding-bottom: 1rem; + + &.withManageButtons { + --_height: 18.5rem; + + .radialPattern { + inset: -8rem -5% -5% -5%; + } + + .sticker { + margin-top: 2.5rem; + } + } + + :global { + canvas { + transition: 250ms transform; + } + } } .subtitleBadge { diff --git a/src/components/modals/gift/UniqueGiftHeader.tsx b/src/components/modals/gift/UniqueGiftHeader.tsx index 560aaafca..58e043974 100644 --- a/src/components/modals/gift/UniqueGiftHeader.tsx +++ b/src/components/modals/gift/UniqueGiftHeader.tsx @@ -4,6 +4,7 @@ import { getActions } from '../../../global'; import type { ApiPeer, + ApiSavedStarGift, ApiStarGiftAttributeBackdrop, ApiStarGiftAttributeModel, ApiStarGiftAttributePattern, ApiTypeCurrencyAmount } from '../../../api/types'; @@ -15,7 +16,7 @@ import buildClassName from '../../../util/buildClassName'; import buildStyle from '../../../util/buildStyle'; import { useTransitionActiveKey } from '../../../hooks/animations/useTransitionActiveKey'; -import useFlag from '../../../hooks/useFlag.ts'; +import useFlag from '../../../hooks/useFlag'; import useLang from '../../../hooks/useLang'; import AnimatedIconFromSticker from '../../common/AnimatedIconFromSticker'; @@ -23,6 +24,7 @@ import Icon from '../../common/icons/Icon'; import StarIcon from '../../common/icons/StarIcon'; import RadialPatternBackground from '../../common/profile/RadialPatternBackground'; import Transition from '../../ui/Transition'; +import UniqueGiftManageButtons from './UniqueGiftManageButtons'; import styles from './UniqueGiftHeader.module.scss'; @@ -35,6 +37,8 @@ type OwnProps = { subtitlePeer?: ApiPeer; className?: string; resellPrice?: ApiTypeCurrencyAmount; + showManageButtons?: boolean; + savedGift?: ApiSavedStarGift; }; const STICKER_SIZE = 120; @@ -48,13 +52,15 @@ const UniqueGiftHeader = ({ subtitlePeer, className, resellPrice, + showManageButtons, + savedGift, }: OwnProps) => { const { openChat, } = getActions(); const lang = useLang(); - const [isHover, markHover, unmarkHover] = useFlag(); + const [isGiftHover, markGiftHover, unmarkGiftHover] = useFlag(false); const activeKey = useTransitionActiveKey([modelAttribute, backdropAttribute, patternAttribute]); const subtitleColor = backdropAttribute?.textColor; @@ -73,10 +79,14 @@ const UniqueGiftHeader = ({ }, [backdropAttribute, patternAttribute]); return ( -
+
{title &&

{title}

} @@ -105,6 +115,11 @@ const UniqueGiftHeader = ({ {subtitle}
)} + {savedGift && showManageButtons && ( + + )} {resellPrice && (

diff --git a/src/components/modals/gift/UniqueGiftManageButtons.module.scss b/src/components/modals/gift/UniqueGiftManageButtons.module.scss new file mode 100644 index 000000000..a8eee242f --- /dev/null +++ b/src/components/modals/gift/UniqueGiftManageButtons.module.scss @@ -0,0 +1,24 @@ +.manageButtons { + z-index: 1; + + display: flex; + gap: 0.5rem; + + width: 100%; + margin-top: 1rem; +} + +.manageButton { + flex-basis: 0; + flex-grow: 1; +} + +.text { + font-size: 0.875rem; + font-weight: var(--font-weight-normal); + text-transform: lowercase; +} + +.icon { + font-size: 1.5rem; +} diff --git a/src/components/modals/gift/UniqueGiftManageButtons.tsx b/src/components/modals/gift/UniqueGiftManageButtons.tsx new file mode 100644 index 000000000..878b91642 --- /dev/null +++ b/src/components/modals/gift/UniqueGiftManageButtons.tsx @@ -0,0 +1,212 @@ +import { memo, useMemo } from '../../../lib/teact/teact'; +import { getActions, getGlobal, withGlobal } from '../../../global'; + +import type { + ApiEmojiStatusCollectible, + ApiEmojiStatusType, + ApiSavedStarGift, +} from '../../../api/types'; + +import { DEFAULT_STATUS_ICON_ID } from '../../../config'; +import { STARS_CURRENCY_CODE } from '../../../config'; +import { selectTabState, selectUser } from '../../../global/selectors'; +import { formatDateAtTime } from '../../../util/dates/dateFormat'; +import { getServerTime } from '../../../util/serverTime'; + +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; +import useOldLang from '../../../hooks/useOldLang'; + +import Button from '../../ui/Button'; + +import styles from './UniqueGiftManageButtons.module.scss'; + +type OwnProps = { + savedGift?: ApiSavedStarGift; +}; + +type StateProps = { + currentUserEmojiStatus?: ApiEmojiStatusType; + collectibleEmojiStatuses?: ApiEmojiStatusType[]; +}; + +const UniqueGiftManageButtons = ({ + savedGift, + currentUserEmojiStatus, + collectibleEmojiStatuses, +}: OwnProps & StateProps) => { + const { + openGiftTransferModal, + openGiftResalePriceComposerModal, + openGiftStatusInfoModal, + setEmojiStatus, + updateStarGiftPrice, + showNotification, + closeGiftInfoModal, + } = getActions(); + + const lang = useLang(); + const oldLang = useOldLang(); + + const gift = savedGift?.gift; + const isGiftUnique = gift?.type === 'starGiftUnique'; + const giftResalePrice = isGiftUnique ? gift.resellPrice : undefined; + + const global = getGlobal(); + const modal = selectTabState(global).giftInfoModal; + const peerId = modal?.peerId; + + const starGiftUniqueSlug = gift?.type === 'starGiftUnique' ? gift.slug : undefined; + const userCollectibleStatus = useMemo(() => { + if (!starGiftUniqueSlug) return undefined; + return collectibleEmojiStatuses?.find((status) => + status.type === 'collectible' && status.slug === starGiftUniqueSlug, + ) as ApiEmojiStatusCollectible | undefined; + }, [starGiftUniqueSlug, collectibleEmojiStatuses]); + + const currentUniqueEmojiStatusSlug = currentUserEmojiStatus?.type === 'collectible' + ? currentUserEmojiStatus.slug : undefined; + const canTakeOff = starGiftUniqueSlug !== undefined && currentUniqueEmojiStatusSlug === starGiftUniqueSlug; + const canWear = Boolean(userCollectibleStatus) && !canTakeOff; + + const handleTransfer = useLastCallback(() => { + if (!savedGift || savedGift?.gift.type !== 'starGiftUnique') return; + + if (savedGift.canTransferAt && savedGift.canTransferAt > getServerTime()) { + showNotification({ + message: { + key: 'NotificationGiftCanTransferAt', + variables: { date: formatDateAtTime(oldLang, savedGift.canTransferAt * 1000) }, + }, + }); + return; + } + + openGiftTransferModal({ gift: savedGift }); + }); + + const handleWear = useLastCallback(() => { + if (canTakeOff) { + setEmojiStatus({ + emojiStatus: { type: 'regular', documentId: DEFAULT_STATUS_ICON_ID }, + }); + } else if (userCollectibleStatus) { + openGiftStatusInfoModal({ emojiStatus: userCollectibleStatus }); + } + }); + + const handleSell = useLastCallback(() => { + if (!savedGift || !peerId) return; + if (savedGift.canResellAt && savedGift.canResellAt > getServerTime()) { + showNotification({ + message: { + key: 'NotificationGiftCanResellAt', + variables: { date: formatDateAtTime(oldLang, savedGift.canResellAt * 1000) }, + }, + }); + return; + } + openGiftResalePriceComposerModal({ peerId, gift: savedGift }); + }); + + const handleUnlist = useLastCallback(() => { + if (!savedGift || savedGift.gift.type !== 'starGiftUnique' || !savedGift.inputGift) return; + closeGiftInfoModal(); + updateStarGiftPrice({ gift: savedGift.inputGift, price: { + currency: STARS_CURRENCY_CODE, amount: 0, nanos: 0, + } }); + showNotification({ + icon: 'unlist-outline', + message: { + key: 'NotificationGiftIsUnlist', + variables: { gift: lang('GiftUnique', { title: savedGift.gift.title, number: savedGift.gift.number }) }, + }, + }); + }); + + return ( +

+ + {(canWear || !canTakeOff) && ( + + )} + {!giftResalePrice && ( + + )} + {Boolean(giftResalePrice) && ( + + )} +
+ ); +}; + +export default memo(withGlobal( + (global): StateProps => { + const { currentUserId } = global; + const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined; + const currentUserEmojiStatus = currentUser?.emojiStatus; + const collectibleEmojiStatuses = global.collectibleEmojiStatuses?.statuses; + + return { + currentUserEmojiStatus, + collectibleEmojiStatuses, + }; + }, +)(UniqueGiftManageButtons)); diff --git a/src/components/modals/gift/info/GiftInfoModal.module.scss b/src/components/modals/gift/info/GiftInfoModal.module.scss index 0b2a283bc..eae2ff5d2 100644 --- a/src/components/modals/gift/info/GiftInfoModal.module.scss +++ b/src/components/modals/gift/info/GiftInfoModal.module.scss @@ -36,11 +36,21 @@ color: var(--color-error); } +.modalContent { + position: relative; + max-height: min(92vh, 46rem) !important; +} + .headerSplitButton { position: absolute; right: 0.375rem; + display: flex; flex-direction: row; + + border-radius: 1rem; + + backdrop-filter: blur(0.5rem); } .headerButton, diff --git a/src/components/modals/gift/info/GiftInfoModal.tsx b/src/components/modals/gift/info/GiftInfoModal.tsx index c3819d87e..d28334652 100644 --- a/src/components/modals/gift/info/GiftInfoModal.tsx +++ b/src/components/modals/gift/info/GiftInfoModal.tsx @@ -145,9 +145,22 @@ const GiftInfoModal = ({ return lang('GiftInfoCollectible', { number: gift.number }); }, [gift, releasedByPeer, lang]); + const starGiftUniqueSlug = gift?.type === 'starGiftUnique' ? gift.slug : undefined; + + const selfCollectibleStatus = useMemo(() => { + if (!starGiftUniqueSlug) return undefined; + return collectibleEmojiStatuses?.find((status) => + status.type === 'collectible' && status.slug === starGiftUniqueSlug); + }, [starGiftUniqueSlug, collectibleEmojiStatuses]); + + const isSelfUnique = Boolean(selfCollectibleStatus); const canFocusUpgrade = Boolean(savedGift?.upgradeMsgId); + const canManage = !canFocusUpgrade && savedGift?.inputGift && ( - isTargetChat ? hasAdminRights : renderingTargetPeer?.id === currentUserId + isTargetChat ? hasAdminRights + : gift?.type === 'starGift' + ? renderingTargetPeer?.id === currentUserId + : gift?.ownerId === currentUserId || isSelfUnique ); function getResalePrice(shouldPayInTon?: boolean) { @@ -164,7 +177,8 @@ const GiftInfoModal = ({ const resellPrice = getResalePrice(); const confirmPrice = getResalePrice(shouldPayInTon); - const canBuyGift = gift?.type === 'starGiftUnique' && gift.ownerId !== currentUserId && Boolean(resellPrice); + const canBuyGift = !isSelfUnique && gift?.type === 'starGiftUnique' + && gift.ownerId !== currentUserId && Boolean(resellPrice); const giftOwnerTitle = (() => { if (!isGiftUnique) return undefined; @@ -277,7 +291,7 @@ const GiftInfoModal = ({ ); } - if (canManage && savedGift.canUpgrade && !savedGift.upgradeMsgId) { + if (canManage && savedGift?.canUpgrade && !savedGift.upgradeMsgId) { return (
); @@ -779,6 +795,7 @@ const GiftInfoModal = ({ tableData={modalData?.tableData} footer={modalData?.footer} className={styles.modal} + contentClassName={styles.modalContent} onClose={handleClose} withBalanceBar={Boolean(canBuyGift)} currencyInBalanceBar={confirmPrice?.currency} diff --git a/src/components/modals/stars/StarsBalanceModal.tsx b/src/components/modals/stars/StarsBalanceModal.tsx index 26459c4f7..798321b6d 100644 --- a/src/components/modals/stars/StarsBalanceModal.tsx +++ b/src/components/modals/stars/StarsBalanceModal.tsx @@ -137,13 +137,13 @@ const StarsBalanceModal = ({ const modalHeight = useMemo(() => { const transactionCount = history?.all?.transactions.length || 0; - if (transactionCount == 1) { + if (transactionCount === 1) { return '35.5rem'; } - if (transactionCount == 2) { + if (transactionCount === 2) { return '39.25rem'; } - if (transactionCount == 3) { + if (transactionCount === 3) { return '43rem'; } return '45rem'; diff --git a/src/components/ui/Button.scss b/src/components/ui/Button.scss index c22b89b67..1c6a627c2 100644 --- a/src/components/ui/Button.scss +++ b/src/components/ui/Button.scss @@ -324,6 +324,16 @@ backdrop-filter: blur(50px); } + &.transparentBlured { + color: white; + background-color: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(0.5rem); + + &:hover { + background-color: rgba(0, 0, 0, 0.1); + } + } + &.smaller { height: 2.5rem; padding: 0.3125rem; @@ -338,23 +348,13 @@ } &.with-icon { - padding-right: 1.25rem; - padding-left: 0.75rem; + padding-inline-start: 0.75rem; + padding-inline-end: 1.25rem; .icon { - margin-right: 0.5rem; + margin-inline-end: 0.5rem; font-size: 1.5rem; } - - &[dir="rtl"] { - padding-right: 0.75rem; - padding-left: 1.25rem; - - .icon { - margin-right: 0; - margin-left: 0.5rem; - } - } } } @@ -391,6 +391,11 @@ } } + &.content-with-icon-bottom, + &.content-with-icon-top { + height: auto; + } + &.fluid { width: auto; padding-right: 1.75rem; @@ -471,4 +476,38 @@ &.rectangular { border-radius: 0; } + + .with-icon-top { + display: flex; + flex-direction: column; + + .icon { + margin-bottom: 0.25rem; + } + } + + .with-icon-bottom { + display: flex; + flex-direction: column-reverse; + + .icon { + margin-top: 0.25rem; + } + } + + .with-icon-start { + display: flex; + .icon { + margin-inline-end: 0.25rem; + } + } + + .with-icon-end { + display: flex; + flex-direction: row-reverse; + + .icon { + margin-inline-start: 0.25rem; + } + } } diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index d0fc52f65..5127b1a59 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -3,6 +3,8 @@ import type { ElementRef, FC } from '../../lib/teact/teact'; import type React from '../../lib/teact/teact'; import { useRef, useState } from '../../lib/teact/teact'; +import type { IconName } from '../../types/icons'; + import { IS_TOUCH_ENV, MouseButton } from '../../util/browser/windowEnvironment'; import buildClassName from '../../util/buildClassName'; import buildStyle from '../../util/buildStyle'; @@ -10,6 +12,7 @@ import buildStyle from '../../util/buildStyle'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; +import Icon from '../common/icons/Icon'; import Sparkles from '../common/Sparkles'; import RippleEffect from './RippleEffect'; import Spinner from './Spinner'; @@ -23,7 +26,7 @@ export type OwnProps = { size?: 'default' | 'smaller' | 'tiny'; color?: ( 'primary' | 'secondary' | 'gray' | 'danger' | 'translucent' | 'translucent-white' | 'translucent-black' - | 'translucent-bordered' | 'dark' | 'green' | 'adaptive' | 'stars' | 'bluredStarsBadge' + | 'translucent-bordered' | 'dark' | 'green' | 'adaptive' | 'stars' | 'bluredStarsBadge' | 'transparentBlured' ); backgroundImage?: string; id?: string; @@ -55,6 +58,9 @@ export type OwnProps = { noForcedUpperCase?: boolean; shouldStopPropagation?: boolean; style?: string; + iconName?: IconName; + iconAlignment?: 'top' | 'bottom' | 'start' | 'end'; + iconClassName?: string; onClick?: (e: ReactMouseEvent) => void; onContextMenu?: (e: ReactMouseEvent) => void; onMouseDown?: (e: ReactMouseEvent) => void; @@ -112,6 +118,9 @@ const Button: FC = ({ shouldStopPropagation, noForcedUpperCase, style, + iconName, + iconAlignment = 'start', + iconClassName, }) => { let elementRef = useRef(); if (ref) { @@ -146,6 +155,7 @@ const Button: FC = ({ withPremiumGradient && 'premium', isRectangular && 'rectangular', noForcedUpperCase && 'no-upper-case', + iconAlignment && iconName && `content-with-icon-${iconAlignment}`, ); const handleClick = useLastCallback((e: ReactMouseEvent) => { @@ -173,15 +183,39 @@ const Button: FC = ({ } }); - const content = ( - <> - {withSparkleEffect && } - {isLoading ? ( + const renderIcon = () => { + if (!iconName) return undefined; + return ; + }; + + const renderContent = () => { + if (isLoading) { + return (
{lang('Cache.ClearProgress')}
- ) : children} + ); + } + + const icon = renderIcon(); + + if (!icon) { + return children; + } + + return ( +
+ {icon} + {children} +
+ ); + }; + + const content = ( + <> + {withSparkleEffect && } + {renderContent()} {!isNotInteractive && ripple && ( )} diff --git a/src/styles/icons.scss b/src/styles/icons.scss index b89d13721..6017a091b 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -133,198 +133,199 @@ $icons-map: ( "frozen-time": "\f160", "fullscreen": "\f161", "gifs": "\f162", - "gift": "\f163", - "group-filled": "\f164", - "group": "\f165", - "grouped-disable": "\f166", - "grouped": "\f167", - "hand-stop": "\f168", - "hashtag": "\f169", - "hd-photo": "\f16a", - "heart-outline": "\f16b", - "heart": "\f16c", - "help": "\f16d", - "info-filled": "\f16e", - "info": "\f16f", - "install": "\f170", - "italic": "\f171", - "key": "\f172", - "keyboard": "\f173", - "lamp": "\f174", - "language": "\f175", - "large-pause": "\f176", - "large-play": "\f177", - "link-badge": "\f178", - "link-broken": "\f179", - "link": "\f17a", - "location": "\f17b", - "lock-badge": "\f17c", - "lock": "\f17d", - "logout": "\f17e", - "loop": "\f17f", - "mention": "\f180", - "message-failed": "\f181", - "message-pending": "\f182", - "message-read": "\f183", - "message-succeeded": "\f184", - "message": "\f185", - "microphone-alt": "\f186", - "microphone": "\f187", - "monospace": "\f188", - "more-circle": "\f189", - "more": "\f18a", - "move-caption-down": "\f18b", - "move-caption-up": "\f18c", - "mute": "\f18d", - "muted": "\f18e", - "my-notes": "\f18f", - "new-chat-filled": "\f190", - "next": "\f191", - "nochannel": "\f192", - "noise-suppression": "\f193", - "non-contacts": "\f194", - "one-filled": "\f195", - "open-in-new-tab": "\f196", - "password-off": "\f197", - "pause": "\f198", - "permissions": "\f199", - "phone-discard-outline": "\f19a", - "phone-discard": "\f19b", - "phone": "\f19c", - "photo": "\f19d", - "pin-badge": "\f19e", - "pin-list": "\f19f", - "pin": "\f1a0", - "pinned-chat": "\f1a1", - "pinned-message": "\f1a2", - "pip": "\f1a3", - "play-story": "\f1a4", - "play": "\f1a5", - "poll": "\f1a6", - "previous": "\f1a7", - "privacy-policy": "\f1a8", - "proof-of-ownership": "\f1a9", - "quote-text": "\f1aa", - "quote": "\f1ab", - "radial-badge": "\f1ac", - "rating-icons-level1": "\f1ad", - "rating-icons-level10": "\f1ae", - "rating-icons-level2": "\f1af", - "rating-icons-level20": "\f1b0", - "rating-icons-level3": "\f1b1", - "rating-icons-level30": "\f1b2", - "rating-icons-level4": "\f1b3", - "rating-icons-level40": "\f1b4", - "rating-icons-level5": "\f1b5", - "rating-icons-level50": "\f1b6", - "rating-icons-level6": "\f1b7", - "rating-icons-level60": "\f1b8", - "rating-icons-level7": "\f1b9", - "rating-icons-level70": "\f1ba", - "rating-icons-level8": "\f1bb", - "rating-icons-level80": "\f1bc", - "rating-icons-level9": "\f1bd", - "rating-icons-level90": "\f1be", - "rating-icons-negative": "\f1bf", - "readchats": "\f1c0", - "recent": "\f1c1", - "refund": "\f1c2", - "reload": "\f1c3", - "remove-quote": "\f1c4", - "remove": "\f1c5", - "reopen-topic": "\f1c6", - "replace": "\f1c7", - "replies": "\f1c8", - "reply-filled": "\f1c9", - "reply": "\f1ca", - "revenue-split": "\f1cb", - "revote": "\f1cc", - "save-story": "\f1cd", - "saved-messages": "\f1ce", - "schedule": "\f1cf", - "sd-photo": "\f1d0", - "search": "\f1d1", - "select": "\f1d2", - "sell-outline": "\f1d3", - "sell": "\f1d4", - "send-outline": "\f1d5", - "send": "\f1d6", - "settings-filled": "\f1d7", - "settings": "\f1d8", - "share-filled": "\f1d9", - "share-screen-outlined": "\f1da", - "share-screen-stop": "\f1db", - "share-screen": "\f1dc", - "show-message": "\f1dd", - "sidebar": "\f1de", - "skip-next": "\f1df", - "skip-previous": "\f1e0", - "smallscreen": "\f1e1", - "smile": "\f1e2", - "sort-by-date": "\f1e3", - "sort-by-number": "\f1e4", - "sort-by-price": "\f1e5", - "sort": "\f1e6", - "speaker-muted-story": "\f1e7", - "speaker-outline": "\f1e8", - "speaker-story": "\f1e9", - "speaker": "\f1ea", - "spoiler-disable": "\f1eb", - "spoiler": "\f1ec", - "sport": "\f1ed", - "star": "\f1ee", - "stars-lock": "\f1ef", - "stats": "\f1f0", - "stealth-future": "\f1f1", - "stealth-past": "\f1f2", - "stickers": "\f1f3", - "stop-raising-hand": "\f1f4", - "stop": "\f1f5", - "story-caption": "\f1f6", - "story-expired": "\f1f7", - "story-priority": "\f1f8", - "story-reply": "\f1f9", - "strikethrough": "\f1fa", - "tag-add": "\f1fb", - "tag-crossed": "\f1fc", - "tag-filter": "\f1fd", - "tag-name": "\f1fe", - "tag": "\f1ff", - "timer": "\f200", - "toncoin": "\f201", - "trade": "\f202", - "transcribe": "\f203", - "truck": "\f204", - "unarchive": "\f205", - "underlined": "\f206", - "understood": "\f207", - "unique-profile": "\f208", - "unlist-outline": "\f209", - "unlist": "\f20a", - "unlock-badge": "\f20b", - "unlock": "\f20c", - "unmute": "\f20d", - "unpin": "\f20e", - "unread": "\f20f", - "up": "\f210", - "user-filled": "\f211", - "user-online": "\f212", - "user-stars": "\f213", - "user": "\f214", - "video-outlined": "\f215", - "video-stop": "\f216", - "video": "\f217", - "view-once": "\f218", - "voice-chat": "\f219", - "volume-1": "\f21a", - "volume-2": "\f21b", - "volume-3": "\f21c", - "warning": "\f21d", - "web": "\f21e", - "webapp": "\f21f", - "word-wrap": "\f220", - "zoom-in": "\f221", - "zoom-out": "\f222", + "gift-transfer-inline": "\f163", + "gift": "\f164", + "group-filled": "\f165", + "group": "\f166", + "grouped-disable": "\f167", + "grouped": "\f168", + "hand-stop": "\f169", + "hashtag": "\f16a", + "hd-photo": "\f16b", + "heart-outline": "\f16c", + "heart": "\f16d", + "help": "\f16e", + "info-filled": "\f16f", + "info": "\f170", + "install": "\f171", + "italic": "\f172", + "key": "\f173", + "keyboard": "\f174", + "lamp": "\f175", + "language": "\f176", + "large-pause": "\f177", + "large-play": "\f178", + "link-badge": "\f179", + "link-broken": "\f17a", + "link": "\f17b", + "location": "\f17c", + "lock-badge": "\f17d", + "lock": "\f17e", + "logout": "\f17f", + "loop": "\f180", + "mention": "\f181", + "message-failed": "\f182", + "message-pending": "\f183", + "message-read": "\f184", + "message-succeeded": "\f185", + "message": "\f186", + "microphone-alt": "\f187", + "microphone": "\f188", + "monospace": "\f189", + "more-circle": "\f18a", + "more": "\f18b", + "move-caption-down": "\f18c", + "move-caption-up": "\f18d", + "mute": "\f18e", + "muted": "\f18f", + "my-notes": "\f190", + "new-chat-filled": "\f191", + "next": "\f192", + "nochannel": "\f193", + "noise-suppression": "\f194", + "non-contacts": "\f195", + "one-filled": "\f196", + "open-in-new-tab": "\f197", + "password-off": "\f198", + "pause": "\f199", + "permissions": "\f19a", + "phone-discard-outline": "\f19b", + "phone-discard": "\f19c", + "phone": "\f19d", + "photo": "\f19e", + "pin-badge": "\f19f", + "pin-list": "\f1a0", + "pin": "\f1a1", + "pinned-chat": "\f1a2", + "pinned-message": "\f1a3", + "pip": "\f1a4", + "play-story": "\f1a5", + "play": "\f1a6", + "poll": "\f1a7", + "previous": "\f1a8", + "privacy-policy": "\f1a9", + "proof-of-ownership": "\f1aa", + "quote-text": "\f1ab", + "quote": "\f1ac", + "radial-badge": "\f1ad", + "rating-icons-level1": "\f1ae", + "rating-icons-level10": "\f1af", + "rating-icons-level2": "\f1b0", + "rating-icons-level20": "\f1b1", + "rating-icons-level3": "\f1b2", + "rating-icons-level30": "\f1b3", + "rating-icons-level4": "\f1b4", + "rating-icons-level40": "\f1b5", + "rating-icons-level5": "\f1b6", + "rating-icons-level50": "\f1b7", + "rating-icons-level6": "\f1b8", + "rating-icons-level60": "\f1b9", + "rating-icons-level7": "\f1ba", + "rating-icons-level70": "\f1bb", + "rating-icons-level8": "\f1bc", + "rating-icons-level80": "\f1bd", + "rating-icons-level9": "\f1be", + "rating-icons-level90": "\f1bf", + "rating-icons-negative": "\f1c0", + "readchats": "\f1c1", + "recent": "\f1c2", + "refund": "\f1c3", + "reload": "\f1c4", + "remove-quote": "\f1c5", + "remove": "\f1c6", + "reopen-topic": "\f1c7", + "replace": "\f1c8", + "replies": "\f1c9", + "reply-filled": "\f1ca", + "reply": "\f1cb", + "revenue-split": "\f1cc", + "revote": "\f1cd", + "save-story": "\f1ce", + "saved-messages": "\f1cf", + "schedule": "\f1d0", + "sd-photo": "\f1d1", + "search": "\f1d2", + "select": "\f1d3", + "sell-outline": "\f1d4", + "sell": "\f1d5", + "send-outline": "\f1d6", + "send": "\f1d7", + "settings-filled": "\f1d8", + "settings": "\f1d9", + "share-filled": "\f1da", + "share-screen-outlined": "\f1db", + "share-screen-stop": "\f1dc", + "share-screen": "\f1dd", + "show-message": "\f1de", + "sidebar": "\f1df", + "skip-next": "\f1e0", + "skip-previous": "\f1e1", + "smallscreen": "\f1e2", + "smile": "\f1e3", + "sort-by-date": "\f1e4", + "sort-by-number": "\f1e5", + "sort-by-price": "\f1e6", + "sort": "\f1e7", + "speaker-muted-story": "\f1e8", + "speaker-outline": "\f1e9", + "speaker-story": "\f1ea", + "speaker": "\f1eb", + "spoiler-disable": "\f1ec", + "spoiler": "\f1ed", + "sport": "\f1ee", + "star": "\f1ef", + "stars-lock": "\f1f0", + "stats": "\f1f1", + "stealth-future": "\f1f2", + "stealth-past": "\f1f3", + "stickers": "\f1f4", + "stop-raising-hand": "\f1f5", + "stop": "\f1f6", + "story-caption": "\f1f7", + "story-expired": "\f1f8", + "story-priority": "\f1f9", + "story-reply": "\f1fa", + "strikethrough": "\f1fb", + "tag-add": "\f1fc", + "tag-crossed": "\f1fd", + "tag-filter": "\f1fe", + "tag-name": "\f1ff", + "tag": "\f200", + "timer": "\f201", + "toncoin": "\f202", + "trade": "\f203", + "transcribe": "\f204", + "truck": "\f205", + "unarchive": "\f206", + "underlined": "\f207", + "understood": "\f208", + "unique-profile": "\f209", + "unlist-outline": "\f20a", + "unlist": "\f20b", + "unlock-badge": "\f20c", + "unlock": "\f20d", + "unmute": "\f20e", + "unpin": "\f20f", + "unread": "\f210", + "up": "\f211", + "user-filled": "\f212", + "user-online": "\f213", + "user-stars": "\f214", + "user": "\f215", + "video-outlined": "\f216", + "video-stop": "\f217", + "video": "\f218", + "view-once": "\f219", + "voice-chat": "\f21a", + "volume-1": "\f21b", + "volume-2": "\f21c", + "volume-3": "\f21d", + "warning": "\f21e", + "web": "\f21f", + "webapp": "\f220", + "word-wrap": "\f221", + "zoom-in": "\f222", + "zoom-out": "\f223", ); .icon-active-sessions::before { @@ -621,6 +622,9 @@ $icons-map: ( .icon-gifs::before { content: map.get($icons-map, "gifs"); } +.icon-gift-transfer-inline::before { + content: map.get($icons-map, "gift-transfer-inline"); +} .icon-gift::before { content: map.get($icons-map, "gift"); } diff --git a/src/styles/icons.woff b/src/styles/icons.woff index 76c161972..d3d03b451 100644 Binary files a/src/styles/icons.woff and b/src/styles/icons.woff differ diff --git a/src/styles/icons.woff2 b/src/styles/icons.woff2 index bc3e457fb..ec48689f7 100644 Binary files a/src/styles/icons.woff2 and b/src/styles/icons.woff2 differ diff --git a/src/types/icons/font.ts b/src/types/icons/font.ts index 5e4364c6e..564785cb6 100644 --- a/src/types/icons/font.ts +++ b/src/types/icons/font.ts @@ -97,6 +97,7 @@ export type FontIconName = | 'frozen-time' | 'fullscreen' | 'gifs' + | 'gift-transfer-inline' | 'gift' | 'group-filled' | 'group' diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 16c00b63c..6c16b32b0 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1230,7 +1230,7 @@ export interface LangPair { 'GiftInfoDescriptionRegular': undefined; 'GiftInfoDescriptionUpgradeRegular': undefined; 'GiftInfoDescriptionFreeUpgrade': undefined; - 'GiftInfoDescriptionUpgrade': undefined; + 'GiftInfoDescriptionUpgrade2': undefined; 'GiftInfoDescriptionUpgraded': undefined; 'GiftInfoFrom': undefined; 'GiftInfoDate': undefined; diff --git a/src/util/colors.ts b/src/util/colors.ts index fcc2ee93a..7d68d7451 100644 --- a/src/util/colors.ts +++ b/src/util/colors.ts @@ -26,7 +26,7 @@ export function rgb2hex(param: [number, number, number]) { const p0 = param[0].toString(16); const p1 = param[1].toString(16); const p2 = param[2].toString(16); - return (p0.length == 1 ? '0' + p0 : p0) + (p1.length == 1 ? '0' + p1 : p1) + (p2.length == 1 ? '0' + p2 : p2); + return (p0.length === 1 ? '0' + p0 : p0) + (p1.length === 1 ? '0' + p1 : p1) + (p2.length === 1 ? '0' + p2 : p2); } /** @@ -49,9 +49,9 @@ export function rgb2hsb([r, g, b]: [number, number, number]): [number, number, n let h!: number, s: number, v: number = max; let d = max - min; - s = max == 0 ? 0 : d / max; + s = max === 0 ? 0 : d / max; - if (max == min) { + if (max === min) { h = 0; // achromatic } else { switch (max) {