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) {