Gift Modal: Support My gifts category (#6316)
This commit is contained in:
parent
104fbb1d54
commit
aacd4a8429
@ -1452,6 +1452,7 @@
|
|||||||
"StarsGiftHeader" = "Send a Gift";
|
"StarsGiftHeader" = "Send a Gift";
|
||||||
"StarsGiftHeaderSelf" = "Buy a Gift";
|
"StarsGiftHeaderSelf" = "Buy a Gift";
|
||||||
"StarGiftDescription" = "Give {user} gifts that can be kept on the profile or converted to Stars.";
|
"StarGiftDescription" = "Give {user} gifts that can be kept on the profile or converted to Stars.";
|
||||||
|
"StarGiftDescriptionCollectibles" = "Collectible gifts are unique digital items you exchange or sell.";
|
||||||
"StarGiftDescriptionSelf" = "Buy yourself a gift to add to your profile or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others.";
|
"StarGiftDescriptionSelf" = "Buy yourself a gift to add to your profile or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others.";
|
||||||
"StarGiftDescriptionChannel" = "Select gift to show appreciation to **{peer}**.";
|
"StarGiftDescriptionChannel" = "Select gift to show appreciation to **{peer}**.";
|
||||||
"GiftLimited" = "limited";
|
"GiftLimited" = "limited";
|
||||||
@ -1583,8 +1584,6 @@
|
|||||||
"StarsAmountText_one" = "{amount} Star";
|
"StarsAmountText_one" = "{amount} Star";
|
||||||
"StarsAmountText_other" = "{amount} Stars";
|
"StarsAmountText_other" = "{amount} Stars";
|
||||||
"AllGiftsCategory" = "All gifts";
|
"AllGiftsCategory" = "All gifts";
|
||||||
"LimitedGiftsCategory" = "Limited";
|
|
||||||
"StockGiftsCategory" = "In Stock";
|
|
||||||
"PremiumGiftDescription" = "Premium";
|
"PremiumGiftDescription" = "Premium";
|
||||||
"SendPaidReaction" = "Send ⭐️{amount}";
|
"SendPaidReaction" = "Send ⭐️{amount}";
|
||||||
"StarsPay" = "Confirm and Pay {amount}";
|
"StarsPay" = "Confirm and Pay {amount}";
|
||||||
@ -2070,7 +2069,8 @@
|
|||||||
"ComposerTitleForwardFrom" = "From: **{users}**";
|
"ComposerTitleForwardFrom" = "From: **{users}**";
|
||||||
"ContextMenuItemMention" = "Mention";
|
"ContextMenuItemMention" = "Mention";
|
||||||
"GiftRibbonResale" = "resale";
|
"GiftRibbonResale" = "resale";
|
||||||
"GiftCategoryResale" = "Resale";
|
"GiftCategoryCollectibles" = "Collectibles";
|
||||||
|
"GiftCategoryMyGifts" = "My Gifts";
|
||||||
"HeaderDescriptionResaleGifts_one" = "{count} for resale";
|
"HeaderDescriptionResaleGifts_one" = "{count} for resale";
|
||||||
"HeaderDescriptionResaleGifts_other" = "{count} for resale";
|
"HeaderDescriptionResaleGifts_other" = "{count} for resale";
|
||||||
"GiftSortByPrice" = "Sort by Price";
|
"GiftSortByPrice" = "Sort by Price";
|
||||||
@ -2285,6 +2285,7 @@
|
|||||||
"GiftValueForSaleOnFragment" = "for sale on Fragment";
|
"GiftValueForSaleOnFragment" = "for sale on Fragment";
|
||||||
"GiftValueForSaleOnTelegram" = "for sale on Telegram";
|
"GiftValueForSaleOnTelegram" = "for sale on Telegram";
|
||||||
"EmbeddedMessageNoCaption" = "Caption removed";
|
"EmbeddedMessageNoCaption" = "Caption removed";
|
||||||
|
"ConfirmBuyGiftForTonDescription" = "The seller only accepts TON as payment.";
|
||||||
"TitleGiftLocked" = "Gift Locked";
|
"TitleGiftLocked" = "Gift Locked";
|
||||||
"GiftLockedMessage" = "This gift is currently only available to earlier Telegram users. It will unlock for your account in about **{relativeDate}**.";
|
"GiftLockedMessage" = "This gift is currently only available to earlier Telegram users. It will unlock for your account in about **{relativeDate}**.";
|
||||||
"QuickPreview" = "Quick Preview";
|
"QuickPreview" = "Quick Preview";
|
||||||
|
|||||||
@ -15,5 +15,6 @@ export { default as GiftUpgradeModal } from '../components/modals/gift/upgrade/G
|
|||||||
export { default as GiftStatusInfoModal } from '../components/modals/gift/status/GiftStatusInfoModal';
|
export { default as GiftStatusInfoModal } from '../components/modals/gift/status/GiftStatusInfoModal';
|
||||||
export { default as GiftWithdrawModal } from '../components/modals/gift/withdraw/GiftWithdrawModal';
|
export { default as GiftWithdrawModal } from '../components/modals/gift/withdraw/GiftWithdrawModal';
|
||||||
export { default as GiftTransferModal } from '../components/modals/gift/transfer/GiftTransferModal';
|
export { default as GiftTransferModal } from '../components/modals/gift/transfer/GiftTransferModal';
|
||||||
|
export { default as GiftTransferConfirmModal } from '../components/modals/gift/transfer/GiftTransferConfirmModal';
|
||||||
export { default as ChatRefundModal } from '../components/modals/stars/chatRefund/ChatRefundModal';
|
export { default as ChatRefundModal } from '../components/modals/stars/chatRefund/ChatRefundModal';
|
||||||
export { default as PriceConfirmModal } from '../components/modals/priceConfirm/PriceConfirmModal';
|
export { default as PriceConfirmModal } from '../components/modals/priceConfirm/PriceConfirmModal';
|
||||||
|
|||||||
@ -217,6 +217,7 @@ const Main = ({
|
|||||||
loadPremiumGifts,
|
loadPremiumGifts,
|
||||||
loadTonGifts,
|
loadTonGifts,
|
||||||
loadStarGifts,
|
loadStarGifts,
|
||||||
|
loadMyCollectibleGifts,
|
||||||
loadDefaultTopicIcons,
|
loadDefaultTopicIcons,
|
||||||
loadAddedStickers,
|
loadAddedStickers,
|
||||||
loadFavoriteStickers,
|
loadFavoriteStickers,
|
||||||
@ -332,6 +333,7 @@ const Main = ({
|
|||||||
loadPremiumGifts();
|
loadPremiumGifts();
|
||||||
loadTonGifts();
|
loadTonGifts();
|
||||||
loadStarGifts();
|
loadStarGifts();
|
||||||
|
loadMyCollectibleGifts();
|
||||||
loadAvailableEffects();
|
loadAvailableEffects();
|
||||||
loadBirthdayNumbersStickers();
|
loadBirthdayNumbersStickers();
|
||||||
loadRestrictedEmojiStickers();
|
loadRestrictedEmojiStickers();
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import GiftLockedModal from './gift/locked/GiftLockedModal.async';
|
|||||||
import GiftRecipientPicker from './gift/recipient/GiftRecipientPicker.async';
|
import GiftRecipientPicker from './gift/recipient/GiftRecipientPicker.async';
|
||||||
import GiftResalePriceComposerModal from './gift/resale/GiftResalePriceComposerModal.async';
|
import GiftResalePriceComposerModal from './gift/resale/GiftResalePriceComposerModal.async';
|
||||||
import GiftStatusInfoModal from './gift/status/GiftStatusInfoModal.async';
|
import GiftStatusInfoModal from './gift/status/GiftStatusInfoModal.async';
|
||||||
|
import GiftTransferConfirmModal from './gift/transfer/GiftTransferConfirmModal.async';
|
||||||
import GiftTransferModal from './gift/transfer/GiftTransferModal.async';
|
import GiftTransferModal from './gift/transfer/GiftTransferModal.async';
|
||||||
import GiftUpgradeModal from './gift/upgrade/GiftUpgradeModal.async';
|
import GiftUpgradeModal from './gift/upgrade/GiftUpgradeModal.async';
|
||||||
import GiftInfoValueModal from './gift/value/GiftInfoValueModal.async';
|
import GiftInfoValueModal from './gift/value/GiftInfoValueModal.async';
|
||||||
@ -95,6 +96,7 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'sharePreparedMessageModal' |
|
'sharePreparedMessageModal' |
|
||||||
'giftStatusInfoModal' |
|
'giftStatusInfoModal' |
|
||||||
'giftTransferModal' |
|
'giftTransferModal' |
|
||||||
|
'giftTransferConfirmModal' |
|
||||||
'chatRefundModal' |
|
'chatRefundModal' |
|
||||||
'priceConfirmModal' |
|
'priceConfirmModal' |
|
||||||
'isFrozenAccountModalOpen' |
|
'isFrozenAccountModalOpen' |
|
||||||
@ -156,6 +158,7 @@ const MODALS: ModalRegistry = {
|
|||||||
preparedMessageModal: PreparedMessageModal,
|
preparedMessageModal: PreparedMessageModal,
|
||||||
sharePreparedMessageModal: SharePreparedMessageModal,
|
sharePreparedMessageModal: SharePreparedMessageModal,
|
||||||
giftTransferModal: GiftTransferModal,
|
giftTransferModal: GiftTransferModal,
|
||||||
|
giftTransferConfirmModal: GiftTransferConfirmModal,
|
||||||
chatRefundModal: ChatRefundModal,
|
chatRefundModal: ChatRefundModal,
|
||||||
priceConfirmModal: PriceConfirmModal,
|
priceConfirmModal: PriceConfirmModal,
|
||||||
isFrozenAccountModalOpen: FrozenAccountModal,
|
isFrozenAccountModalOpen: FrozenAccountModal,
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
margin-inline: 0.125rem;
|
||||||
padding: 0.625rem;
|
padding: 0.625rem;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
border-radius: 0.625rem;
|
border-radius: 0.625rem;
|
||||||
@ -69,6 +70,10 @@
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transferBadge {
|
||||||
|
font-size: 0.8125rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.star {
|
.star {
|
||||||
margin-inline-start: 0 !important;
|
margin-inline-start: 0 !important;
|
||||||
margin-inline-end: 0.125rem;
|
margin-inline-end: 0.125rem;
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export type OwnProps = {
|
|||||||
observeIntersection?: ObserveFn;
|
observeIntersection?: ObserveFn;
|
||||||
onClick: (gift: ApiStarGift, target: 'original' | 'resell') => void;
|
onClick: (gift: ApiStarGift, target: 'original' | 'resell') => void;
|
||||||
isResale?: boolean;
|
isResale?: boolean;
|
||||||
|
withTransferBadge?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -38,7 +39,7 @@ type StateProps = {
|
|||||||
const GIFT_STICKER_SIZE = 90;
|
const GIFT_STICKER_SIZE = 90;
|
||||||
|
|
||||||
function GiftItemStar({
|
function GiftItemStar({
|
||||||
gift, observeIntersection, onClick, isResale, isCurrentUserPremium,
|
gift, observeIntersection, onClick, isResale, isCurrentUserPremium, withTransferBadge,
|
||||||
}: OwnProps & StateProps) {
|
}: OwnProps & StateProps) {
|
||||||
const { openGiftInfoModal, openPremiumModal, showNotification, checkCanSendGift } = getActions();
|
const { openGiftInfoModal, openPremiumModal, showNotification, checkCanSendGift } = getActions();
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ function GiftItemStar({
|
|||||||
: getPriceAmount(uniqueGift?.resellPrice);
|
: getPriceAmount(uniqueGift?.resellPrice);
|
||||||
const priceCurrency = priceInfo?.currency || STARS_CURRENCY_CODE;
|
const priceCurrency = priceInfo?.currency || STARS_CURRENCY_CODE;
|
||||||
const resellMinStars = regularGift?.resellMinStars;
|
const resellMinStars = regularGift?.resellMinStars;
|
||||||
const priceInStarsAsString = !isGiftUnique && isResale && resellMinStars
|
const formattedPrice = !isGiftUnique && isResale && resellMinStars
|
||||||
? lang.number(resellMinStars) + '+' : priceInfo?.amount || 0;
|
? lang.number(resellMinStars) + '+' : priceInfo?.amount || 0;
|
||||||
const isLimited = !isGiftUnique && Boolean(regularGift?.isLimited);
|
const isLimited = !isGiftUnique && Boolean(regularGift?.isLimited);
|
||||||
const isSoldOut = !isGiftUnique && Boolean(regularGift?.isSoldOut);
|
const isSoldOut = !isGiftUnique && Boolean(regularGift?.isSoldOut);
|
||||||
@ -175,6 +176,24 @@ function GiftItemStar({
|
|||||||
setIsVisible(visible);
|
setIsVisible(visible);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const badgeContent = useMemo(() => {
|
||||||
|
if (withTransferBadge) {
|
||||||
|
return lang('GiftTransferTitle');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priceCurrency === TON_CURRENCY_CODE) {
|
||||||
|
return formatTonAsIcon(lang, formattedPrice || 0, {
|
||||||
|
shouldConvertFromNanos: true,
|
||||||
|
className: styles.star,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatStarsAsIcon(lang, formattedPrice || 0, {
|
||||||
|
asFont: true,
|
||||||
|
className: styles.star,
|
||||||
|
});
|
||||||
|
}, [withTransferBadge, priceCurrency, formattedPrice, lang]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -212,17 +231,18 @@ function GiftItemStar({
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className={styles.buy}
|
className={buildClassName(
|
||||||
|
styles.buy,
|
||||||
|
withTransferBadge && styles.transferBadge,
|
||||||
|
)}
|
||||||
nonInteractive
|
nonInteractive
|
||||||
size="tiny"
|
size="tiny"
|
||||||
color={isGiftUnique ? 'bluredStarsBadge' : 'stars'}
|
color={isGiftUnique ? 'bluredStarsBadge' : 'stars'}
|
||||||
withSparkleEffect={isVisible}
|
withSparkleEffect={isVisible && !withTransferBadge}
|
||||||
pill
|
pill
|
||||||
fluid
|
fluid
|
||||||
>
|
>
|
||||||
{priceCurrency === TON_CURRENCY_CODE
|
{badgeContent}
|
||||||
? formatTonAsIcon(lang, priceInStarsAsString || 0, { shouldConvertFromNanos: true, className: styles.star })
|
|
||||||
: formatStarsAsIcon(lang, priceInStarsAsString || 0, { asFont: true, className: styles.star })}
|
|
||||||
</Button>
|
</Button>
|
||||||
{giftRibbon}
|
{giftRibbon}
|
||||||
{isLocked && <Icon name="lock-badge" className={styles.lockIcon} />}
|
{isLocked && <Icon name="lock-badge" className={styles.lockIcon} />}
|
||||||
|
|||||||
@ -81,7 +81,7 @@
|
|||||||
.starGiftsContainer,
|
.starGiftsContainer,
|
||||||
.premiumGiftsGallery {
|
.premiumGiftsGallery {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.625rem;
|
gap: 0.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import type {
|
|||||||
ApiDisallowedGifts,
|
ApiDisallowedGifts,
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiPremiumGiftCodeOption,
|
ApiPremiumGiftCodeOption,
|
||||||
|
ApiSavedStarGift,
|
||||||
ApiStarGift,
|
ApiStarGift,
|
||||||
ApiStarGiftRegular,
|
ApiStarGiftRegular,
|
||||||
ApiStarsAmount,
|
ApiStarsAmount,
|
||||||
@ -34,6 +35,7 @@ import Avatar from '../../common/Avatar';
|
|||||||
import InteractiveSparkles from '../../common/InteractiveSparkles';
|
import InteractiveSparkles from '../../common/InteractiveSparkles';
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
import Transition from '../../ui/Transition';
|
import Transition from '../../ui/Transition';
|
||||||
import BalanceBlock from '../stars/BalanceBlock';
|
import BalanceBlock from '../stars/BalanceBlock';
|
||||||
@ -56,6 +58,8 @@ type StateProps = {
|
|||||||
boostPerSentGift?: number;
|
boostPerSentGift?: number;
|
||||||
starGiftsById?: Record<string, ApiStarGiftRegular>;
|
starGiftsById?: Record<string, ApiStarGiftRegular>;
|
||||||
starGiftIdsByCategory?: Record<StarGiftCategory, string[]>;
|
starGiftIdsByCategory?: Record<StarGiftCategory, string[]>;
|
||||||
|
myCollectibleGiftsById?: Record<string, ApiSavedStarGift>;
|
||||||
|
myCollectibleGiftIds?: string[];
|
||||||
starBalance?: ApiStarsAmount;
|
starBalance?: ApiStarsAmount;
|
||||||
peer?: ApiPeer;
|
peer?: ApiPeer;
|
||||||
isSelf?: boolean;
|
isSelf?: boolean;
|
||||||
@ -77,6 +81,8 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
modal,
|
modal,
|
||||||
starGiftsById,
|
starGiftsById,
|
||||||
starGiftIdsByCategory,
|
starGiftIdsByCategory,
|
||||||
|
myCollectibleGiftsById,
|
||||||
|
myCollectibleGiftIds,
|
||||||
starBalance,
|
starBalance,
|
||||||
peer,
|
peer,
|
||||||
isSelf,
|
isSelf,
|
||||||
@ -87,7 +93,14 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
tabId,
|
tabId,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
closeGiftModal, openGiftInfoModal, resetResaleGifts, loadResaleGifts, openGiftInMarket, closeResaleGiftsMarket,
|
closeGiftModal,
|
||||||
|
openGiftInfoModal,
|
||||||
|
resetResaleGifts,
|
||||||
|
loadResaleGifts,
|
||||||
|
openGiftInMarket,
|
||||||
|
closeResaleGiftsMarket,
|
||||||
|
loadMyCollectibleGifts,
|
||||||
|
openGiftTransferConfirmModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const dialogRef = useRef<HTMLDivElement>();
|
const dialogRef = useRef<HTMLDivElement>();
|
||||||
const transitionRef = useRef<HTMLDivElement>();
|
const transitionRef = useRef<HTMLDivElement>();
|
||||||
@ -122,6 +135,7 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const areUnlimitedStarGiftsDisallowed = !isSelf && disallowedGifts?.shouldDisallowUnlimitedStarGifts;
|
const areUnlimitedStarGiftsDisallowed = !isSelf && disallowedGifts?.shouldDisallowUnlimitedStarGifts;
|
||||||
const areLimitedStarGiftsDisallowed = !isSelf && disallowedGifts?.shouldDisallowLimitedStarGifts;
|
const areLimitedStarGiftsDisallowed = !isSelf && disallowedGifts?.shouldDisallowLimitedStarGifts;
|
||||||
|
const areUniqueStarGiftsDisallowed = !isSelf && disallowedGifts?.shouldDisallowUniqueStarGifts;
|
||||||
|
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -206,19 +220,29 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
),
|
),
|
||||||
}, { withNodes: true });
|
}, { withNodes: true });
|
||||||
|
|
||||||
const starGiftDescription = chat
|
const starGiftDescription = useMemo(() => {
|
||||||
? lang('StarGiftDescriptionChannel', { peer: getPeerTitle(lang, chat) }, {
|
if (chat) {
|
||||||
withNodes: true,
|
return lang('StarGiftDescriptionChannel', { peer: getPeerTitle(lang, chat) }, {
|
||||||
withMarkdown: true,
|
withNodes: true,
|
||||||
})
|
withMarkdown: true,
|
||||||
: isSelf
|
});
|
||||||
? lang('StarGiftDescriptionSelf', undefined, {
|
}
|
||||||
|
|
||||||
|
if (isSelf) {
|
||||||
|
return lang('StarGiftDescriptionSelf', undefined, {
|
||||||
withNodes: true,
|
withNodes: true,
|
||||||
renderTextFilters: ['br'],
|
renderTextFilters: ['br'],
|
||||||
})
|
});
|
||||||
: lang('StarGiftDescription', {
|
}
|
||||||
user: getUserFullName(user)!,
|
|
||||||
}, { withNodes: true, withMarkdown: true });
|
if (selectedCategory === 'resale') {
|
||||||
|
return lang('StarGiftDescriptionCollectibles');
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang('StarGiftDescription', {
|
||||||
|
user: getUserFullName(user)!,
|
||||||
|
}, { withNodes: true, withMarkdown: true });
|
||||||
|
}, [chat, isSelf, selectedCategory, user, lang]);
|
||||||
|
|
||||||
function renderGiftPremiumHeader() {
|
function renderGiftPremiumHeader() {
|
||||||
return (
|
return (
|
||||||
@ -268,22 +292,62 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
setIsGiftScreenHeaderForStarGifts('id' in gift);
|
setIsGiftScreenHeaderForStarGifts('id' in gift);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleMyGiftClick = useLastCallback((gift: ApiStarGift) => {
|
||||||
|
if (gift.type === 'starGift' || !myCollectibleGiftsById || !peer?.id) return;
|
||||||
|
const savedGift = myCollectibleGiftsById[gift.id];
|
||||||
|
|
||||||
|
openGiftTransferConfirmModal({
|
||||||
|
gift: savedGift,
|
||||||
|
recipientId: peer.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleLoadMore = useLastCallback(() => {
|
||||||
|
if (selectedCategory === 'myCollectibles') {
|
||||||
|
loadMyCollectibleGifts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function renderStarGifts() {
|
function renderStarGifts() {
|
||||||
|
if (selectedCategory === 'myCollectibles') {
|
||||||
|
return (
|
||||||
|
<InfiniteScroll
|
||||||
|
className={styles.starGiftsContainer}
|
||||||
|
items={myCollectibleGiftIds}
|
||||||
|
onLoadMore={handleLoadMore}
|
||||||
|
scrollContainerClosest={`.${styles.main}`}
|
||||||
|
itemSelector=".starGiftItem"
|
||||||
|
>
|
||||||
|
{myCollectibleGiftsById && myCollectibleGiftIds?.map((giftId) => {
|
||||||
|
const savedGift = myCollectibleGiftsById[giftId];
|
||||||
|
if (!savedGift) return undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GiftItemStar
|
||||||
|
key={giftId}
|
||||||
|
gift={savedGift.gift}
|
||||||
|
observeIntersection={observeIntersection}
|
||||||
|
onClick={handleMyGiftClick}
|
||||||
|
withTransferBadge
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</InfiniteScroll>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const filteredGiftIds = starGiftIdsByCategory?.[selectedCategory]?.filter((giftId) => {
|
const filteredGiftIds = starGiftIdsByCategory?.[selectedCategory]?.filter((giftId) => {
|
||||||
const gift = starGiftsById?.[giftId];
|
const gift = starGiftsById?.[giftId];
|
||||||
if (!gift) return false;
|
if (!gift) return false;
|
||||||
|
|
||||||
const { isLimited, isSoldOut, upgradeStars } = gift;
|
const { isLimited, availabilityResale } = gift;
|
||||||
if (areUnlimitedStarGiftsDisallowed && !areLimitedStarGiftsDisallowed) {
|
|
||||||
return isLimited;
|
if (areLimitedStarGiftsDisallowed && isLimited) {
|
||||||
}
|
return !areUniqueStarGiftsDisallowed ? availabilityResale : false;
|
||||||
if (areLimitedStarGiftsDisallowed && !areUnlimitedStarGiftsDisallowed) {
|
|
||||||
return !isLimited && !isSoldOut;
|
|
||||||
}
|
|
||||||
if (areUnlimitedStarGiftsDisallowed && areLimitedStarGiftsDisallowed) {
|
|
||||||
return Boolean(isLimited && Boolean(upgradeStars));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (areUnlimitedStarGiftsDisallowed && !isLimited) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -291,8 +355,9 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
<div className={styles.starGiftsContainer}>
|
<div className={styles.starGiftsContainer}>
|
||||||
{starGiftsById && filteredGiftIds?.flatMap((giftId) => {
|
{starGiftsById && filteredGiftIds?.flatMap((giftId) => {
|
||||||
const gift = starGiftsById[giftId];
|
const gift = starGiftsById[giftId];
|
||||||
const shouldShowResale = selectedCategory !== 'stock' && Boolean(gift.availabilityResale);
|
const shouldShowResale = Boolean(gift.availabilityResale) && !areUniqueStarGiftsDisallowed;
|
||||||
const shouldDuplicateAsResale = selectedCategory !== 'resale' && shouldShowResale && !gift.isSoldOut;
|
const shouldDuplicateAsResale = selectedCategory !== 'resale'
|
||||||
|
&& shouldShowResale && !gift.isSoldOut && !areLimitedStarGiftsDisallowed;
|
||||||
|
|
||||||
const elements = [
|
const elements = [
|
||||||
<GiftItemStar
|
<GiftItemStar
|
||||||
@ -399,7 +464,9 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
{renderStarGiftsHeader()}
|
{renderStarGiftsHeader()}
|
||||||
{renderStarGiftsDescription()}
|
{renderStarGiftsDescription()}
|
||||||
<StarGiftCategoryList
|
<StarGiftCategoryList
|
||||||
areLimitedStarGiftsDisallowed={areLimitedStarGiftsDisallowed}
|
areCollectibleStarGiftsDisallowed={areUniqueStarGiftsDisallowed}
|
||||||
|
isSelf={isSelf}
|
||||||
|
hasCollectible={Boolean(myCollectibleGiftIds?.length)}
|
||||||
onCategoryChanged={onCategoryChanged}
|
onCategoryChanged={onCategoryChanged}
|
||||||
/>
|
/>
|
||||||
<Transition
|
<Transition
|
||||||
@ -535,6 +602,8 @@ export default memo(withGlobal<OwnProps>((global, { modal }): Complete<StateProp
|
|||||||
boostPerSentGift: global.appConfig.boostsPerSentGift,
|
boostPerSentGift: global.appConfig.boostsPerSentGift,
|
||||||
starGiftsById: starGifts?.byId,
|
starGiftsById: starGifts?.byId,
|
||||||
starGiftIdsByCategory: starGifts?.idsByCategory,
|
starGiftIdsByCategory: starGifts?.idsByCategory,
|
||||||
|
myCollectibleGiftsById: global.myCollectibleGifts?.byId,
|
||||||
|
myCollectibleGiftIds: global.myCollectibleGifts?.ids,
|
||||||
starBalance: stars?.balance,
|
starBalance: stars?.balance,
|
||||||
peer,
|
peer,
|
||||||
isSelf,
|
isSelf,
|
||||||
@ -548,8 +617,6 @@ export default memo(withGlobal<OwnProps>((global, { modal }): Complete<StateProp
|
|||||||
|
|
||||||
function getCategoryKey(category: StarGiftCategory) {
|
function getCategoryKey(category: StarGiftCategory) {
|
||||||
if (category === 'all') return 0;
|
if (category === 'all') return 0;
|
||||||
if (category === 'limited') return 1;
|
if (category === 'myCollectibles') return 1;
|
||||||
if (category === 'resale') return 2;
|
return 2;
|
||||||
if (category === 'stock') return 3;
|
|
||||||
return category + 3;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
gap: 0.0625rem;
|
gap: 0.0625rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: center;
|
||||||
|
|
||||||
// Prevent first item from being always partially obscured
|
// Prevent first item from being always partially obscured
|
||||||
margin-left: -0.5rem;
|
margin-left: -0.5rem;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
memo, useMemo, useRef, useState,
|
memo, useRef, useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../../global';
|
import { withGlobal } from '../../../global';
|
||||||
|
|
||||||
@ -10,12 +10,12 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
|
import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import StarIcon from '../../common/icons/StarIcon';
|
|
||||||
|
|
||||||
import styles from './StarGiftCategoryList.module.scss';
|
import styles from './StarGiftCategoryList.module.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
areLimitedStarGiftsDisallowed?: boolean;
|
areCollectibleStarGiftsDisallowed?: boolean;
|
||||||
|
isSelf?: boolean;
|
||||||
|
hasCollectible?: boolean;
|
||||||
onCategoryChanged: (category: StarGiftCategory) => void;
|
onCategoryChanged: (category: StarGiftCategory) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,16 +26,13 @@ type StateProps = {
|
|||||||
const StarGiftCategoryList = ({
|
const StarGiftCategoryList = ({
|
||||||
idsByCategory,
|
idsByCategory,
|
||||||
onCategoryChanged,
|
onCategoryChanged,
|
||||||
areLimitedStarGiftsDisallowed,
|
areCollectibleStarGiftsDisallowed,
|
||||||
|
isSelf,
|
||||||
|
hasCollectible,
|
||||||
}: StateProps & OwnProps) => {
|
}: StateProps & OwnProps) => {
|
||||||
const ref = useRef<HTMLDivElement>();
|
const ref = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const starCategories: number[] | undefined = useMemo(() => idsByCategory && Object.keys(idsByCategory)
|
|
||||||
.filter((category) => category !== 'all' && category !== 'limited')
|
|
||||||
.map(Number)
|
|
||||||
.sort((a, b) => a - b),
|
|
||||||
[idsByCategory]);
|
|
||||||
|
|
||||||
const hasResale = idsByCategory && idsByCategory['resale'].length > 0;
|
const hasResale = idsByCategory && idsByCategory['resale'].length > 0;
|
||||||
|
|
||||||
@ -50,9 +47,8 @@ const StarGiftCategoryList = ({
|
|||||||
|
|
||||||
function renderCategoryName(category: StarGiftCategory) {
|
function renderCategoryName(category: StarGiftCategory) {
|
||||||
if (category === 'all') return lang('AllGiftsCategory');
|
if (category === 'all') return lang('AllGiftsCategory');
|
||||||
if (category === 'stock') return lang('StockGiftsCategory');
|
if (category === 'myCollectibles') return lang('GiftCategoryMyGifts');
|
||||||
if (category === 'limited') return lang('LimitedGiftsCategory');
|
if (category === 'resale') return lang('GiftCategoryCollectibles');
|
||||||
if (category === 'resale') return lang('GiftCategoryResale');
|
|
||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,13 +61,6 @@ const StarGiftCategoryList = ({
|
|||||||
)}
|
)}
|
||||||
onClick={() => handleItemClick(category)}
|
onClick={() => handleItemClick(category)}
|
||||||
>
|
>
|
||||||
{Number.isInteger(category) && (
|
|
||||||
<StarIcon
|
|
||||||
className={styles.star}
|
|
||||||
type="gold"
|
|
||||||
size="middle"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{renderCategoryName(category)}
|
{renderCategoryName(category)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -82,10 +71,8 @@ const StarGiftCategoryList = ({
|
|||||||
return (
|
return (
|
||||||
<div ref={ref} className={buildClassName(styles.list, 'no-scrollbar')}>
|
<div ref={ref} className={buildClassName(styles.list, 'no-scrollbar')}>
|
||||||
{renderCategoryItem('all')}
|
{renderCategoryItem('all')}
|
||||||
{!areLimitedStarGiftsDisallowed && renderCategoryItem('limited')}
|
{!areCollectibleStarGiftsDisallowed && !isSelf && hasCollectible && renderCategoryItem('myCollectibles')}
|
||||||
{!areLimitedStarGiftsDisallowed && hasResale && renderCategoryItem('resale')}
|
{!areCollectibleStarGiftsDisallowed && hasResale && renderCategoryItem('resale')}
|
||||||
{renderCategoryItem('stock')}
|
|
||||||
{starCategories?.map(renderCategoryItem)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,6 +19,17 @@
|
|||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.descriptionConfirm {
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleConfirm {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -938,10 +938,19 @@ const GiftInfoModal = ({
|
|||||||
confirmHandler={handleConfirmBuyGift}
|
confirmHandler={handleConfirmBuyGift}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
{uniqueGift.resaleTonOnly
|
||||||
|
&& (
|
||||||
|
<div className={styles.descriptionConfirm}>
|
||||||
|
{lang('ConfirmBuyGiftForTonDescription')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<GiftTransferPreview
|
<GiftTransferPreview
|
||||||
peer={recipientPeer || currentUser}
|
peer={recipientPeer || currentUser}
|
||||||
gift={uniqueGift}
|
gift={uniqueGift}
|
||||||
/>
|
/>
|
||||||
|
<div className={styles.titleConfirm}>
|
||||||
|
{lang('TitleConfirmPayment')}
|
||||||
|
</div>
|
||||||
{!recipientPeer
|
{!recipientPeer
|
||||||
&& (
|
&& (
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './GiftTransferConfirmModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const GiftTransferConfirmModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const GiftTransferConfirmModal = useModuleLoader(
|
||||||
|
Bundles.Stars,
|
||||||
|
'GiftTransferConfirmModal',
|
||||||
|
!modal,
|
||||||
|
);
|
||||||
|
|
||||||
|
return GiftTransferConfirmModal ? <GiftTransferConfirmModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GiftTransferConfirmModalAsync;
|
||||||
108
src/components/modals/gift/transfer/GiftTransferConfirmModal.tsx
Normal file
108
src/components/modals/gift/transfer/GiftTransferConfirmModal.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
|
import type { ApiPeer } from '../../../../api/types';
|
||||||
|
import type { TabState } from '../../../../global/types';
|
||||||
|
|
||||||
|
import { getPeerTitle } from '../../../../global/helpers/peers';
|
||||||
|
import { selectPeer } from '../../../../global/selectors';
|
||||||
|
import { formatStarsAsIcon, formatStarsAsText } from '../../../../util/localization/format';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import GiftTransferPreview from '../../../common/gift/GiftTransferPreview';
|
||||||
|
import ConfirmDialog from '../../../ui/ConfirmDialog';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['giftTransferConfirmModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
selectedPeer?: ApiPeer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const GiftTransferConfirmModal = ({ modal, selectedPeer }: OwnProps & StateProps) => {
|
||||||
|
const {
|
||||||
|
closeGiftTransferConfirmModal, transferGift, openChat, closeGiftModal, closeGiftTransferModal,
|
||||||
|
} = getActions();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const renderingSelectedPeer = useCurrentOrPrev(selectedPeer);
|
||||||
|
|
||||||
|
const handleConfirm = useLastCallback(() => {
|
||||||
|
if (!renderingModal?.gift.inputGift || !renderingModal.recipientId) return;
|
||||||
|
|
||||||
|
transferGift({
|
||||||
|
gift: renderingModal.gift.inputGift,
|
||||||
|
recipientId: renderingModal.recipientId,
|
||||||
|
transferStars: renderingModal.gift.transferStars,
|
||||||
|
});
|
||||||
|
|
||||||
|
closeGiftTransferConfirmModal();
|
||||||
|
openChat({ id: renderingModal.recipientId });
|
||||||
|
|
||||||
|
closeGiftModal();
|
||||||
|
closeGiftTransferModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!renderingModal) return undefined;
|
||||||
|
|
||||||
|
const { gift } = renderingModal;
|
||||||
|
const uniqueGift = gift.gift.type === 'starGiftUnique' ? gift.gift : undefined;
|
||||||
|
|
||||||
|
if (!uniqueGift) return undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
isOpen={isOpen}
|
||||||
|
noDefaultTitle
|
||||||
|
onClose={closeGiftTransferConfirmModal}
|
||||||
|
confirmLabel={gift.transferStars
|
||||||
|
? lang(
|
||||||
|
'GiftTransferConfirmButton',
|
||||||
|
{ amount: formatStarsAsIcon(lang, gift.transferStars, { asFont: true }) },
|
||||||
|
{ withNodes: true },
|
||||||
|
) : lang('GiftTransferConfirmButtonFree')}
|
||||||
|
confirmHandler={handleConfirm}
|
||||||
|
>
|
||||||
|
{renderingSelectedPeer && (
|
||||||
|
<GiftTransferPreview
|
||||||
|
peer={renderingSelectedPeer}
|
||||||
|
gift={uniqueGift}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
{gift.transferStars
|
||||||
|
? lang('GiftTransferConfirmDescription', {
|
||||||
|
gift: lang('GiftUnique', { title: uniqueGift.title, number: uniqueGift.number }),
|
||||||
|
amount: formatStarsAsText(lang, gift.transferStars),
|
||||||
|
peer: getPeerTitle(lang, renderingSelectedPeer!),
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
})
|
||||||
|
: lang('GiftTransferConfirmDescriptionFree', {
|
||||||
|
gift: lang('GiftUnique', { title: uniqueGift.title, number: uniqueGift.number }),
|
||||||
|
peer: getPeerTitle(lang, renderingSelectedPeer!),
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(
|
||||||
|
withGlobal<OwnProps>((global, { modal }): Complete<StateProps> => {
|
||||||
|
const selectedPeer = modal?.recipientId ? selectPeer(global, modal.recipientId) : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedPeer,
|
||||||
|
};
|
||||||
|
})(GiftTransferConfirmModal),
|
||||||
|
);
|
||||||
@ -1,19 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
memo, useEffect, useMemo, useState,
|
memo, useMemo, useState,
|
||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import type { ApiStarGiftUnique } from '../../../../api/types';
|
|
||||||
import type { TabState } from '../../../../global/types';
|
import type { TabState } from '../../../../global/types';
|
||||||
import type { UniqueCustomPeer } from '../../../../types';
|
import type { UniqueCustomPeer } from '../../../../types';
|
||||||
|
|
||||||
import { ALL_FOLDER_ID } from '../../../../config';
|
import { ALL_FOLDER_ID } from '../../../../config';
|
||||||
import { getPeerTitle } from '../../../../global/helpers/peers';
|
import { selectCanGift } from '../../../../global/selectors';
|
||||||
import { selectCanGift, selectPeer } from '../../../../global/selectors';
|
|
||||||
import { unique } from '../../../../util/iteratees';
|
import { unique } from '../../../../util/iteratees';
|
||||||
import { formatStarsAsIcon, formatStarsAsText } from '../../../../util/localization/format';
|
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../../../util/memo';
|
||||||
import { getGiftAttributes } from '../../../common/helpers/gifts';
|
|
||||||
import sortChatIds from '../../../common/helpers/sortChatIds';
|
import sortChatIds from '../../../common/helpers/sortChatIds';
|
||||||
|
|
||||||
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||||
@ -22,10 +18,8 @@ import useLang from '../../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../../hooks/useLastCallback';
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
import usePeerSearch from '../../../../hooks/usePeerSearch';
|
import usePeerSearch from '../../../../hooks/usePeerSearch';
|
||||||
|
|
||||||
import GiftTransferPreview from '../../../common/gift/GiftTransferPreview';
|
|
||||||
import PeerPicker from '../../../common/pickers/PeerPicker';
|
import PeerPicker from '../../../common/pickers/PeerPicker';
|
||||||
import PickerModal from '../../../common/pickers/PickerModal';
|
import PickerModal from '../../../common/pickers/PickerModal';
|
||||||
import ConfirmDialog from '../../../ui/ConfirmDialog';
|
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
modal: TabState['giftTransferModal'];
|
modal: TabState['giftTransferModal'];
|
||||||
@ -41,7 +35,11 @@ type Categories = 'withdraw';
|
|||||||
const GiftTransferModal = ({
|
const GiftTransferModal = ({
|
||||||
modal, contactIds, currentUserId,
|
modal, contactIds, currentUserId,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { closeGiftTransferModal, openGiftWithdrawModal, transferGift } = getActions();
|
const {
|
||||||
|
closeGiftTransferModal,
|
||||||
|
openGiftWithdrawModal,
|
||||||
|
openGiftTransferConfirmModal,
|
||||||
|
} = getActions();
|
||||||
const isOpen = Boolean(modal);
|
const isOpen = Boolean(modal);
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -49,16 +47,6 @@ const GiftTransferModal = ({
|
|||||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||||
|
|
||||||
const renderingModal = useCurrentOrPrev(modal);
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
const uniqueGift = renderingModal?.gift?.gift as ApiStarGiftUnique;
|
|
||||||
const giftAttributes = uniqueGift && getGiftAttributes(uniqueGift);
|
|
||||||
|
|
||||||
const [selectedId, setSelectedId] = useState<string | undefined>();
|
|
||||||
|
|
||||||
const renderingSelectedPeerId = useCurrentOrPrev(selectedId);
|
|
||||||
const renderingSelectedPeer = useMemo(() => {
|
|
||||||
const global = getGlobal();
|
|
||||||
return renderingSelectedPeerId ? selectPeer(global, renderingSelectedPeerId) : undefined;
|
|
||||||
}, [renderingSelectedPeerId]);
|
|
||||||
|
|
||||||
const orderedChatIds = useFolderManagerForOrderedIds(ALL_FOLDER_ID);
|
const orderedChatIds = useFolderManagerForOrderedIds(ALL_FOLDER_ID);
|
||||||
|
|
||||||
@ -107,26 +95,13 @@ const GiftTransferModal = ({
|
|||||||
false);
|
false);
|
||||||
}, [isLoading, foundIds, currentUserId]);
|
}, [isLoading, foundIds, currentUserId]);
|
||||||
|
|
||||||
const closeConfirmModal = useLastCallback(() => {
|
const handlePeerSelect = useLastCallback((peerId: string) => {
|
||||||
setSelectedId(undefined);
|
if (!renderingModal?.gift) return;
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
openGiftTransferConfirmModal({
|
||||||
if (!isOpen) {
|
gift: renderingModal.gift,
|
||||||
setSelectedId(undefined);
|
recipientId: peerId,
|
||||||
}
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
const handleTransfer = useLastCallback(() => {
|
|
||||||
if (!renderingModal?.gift.inputGift) return;
|
|
||||||
transferGift({
|
|
||||||
gift: renderingModal.gift.inputGift,
|
|
||||||
recipientId: renderingSelectedPeerId!,
|
|
||||||
transferStars: renderingModal.gift.transferStars,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
closeConfirmModal();
|
|
||||||
closeGiftTransferModal();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -151,47 +126,8 @@ const GiftTransferModal = ({
|
|||||||
filterValue={searchQuery}
|
filterValue={searchQuery}
|
||||||
filterPlaceholder={lang('Search')}
|
filterPlaceholder={lang('Search')}
|
||||||
onFilterChange={setSearchQuery}
|
onFilterChange={setSearchQuery}
|
||||||
onSelectedIdChange={setSelectedId}
|
onSelectedIdChange={handlePeerSelect}
|
||||||
/>
|
/>
|
||||||
{giftAttributes && (
|
|
||||||
<ConfirmDialog
|
|
||||||
isOpen={Boolean(selectedId)}
|
|
||||||
noDefaultTitle
|
|
||||||
onClose={closeConfirmModal}
|
|
||||||
confirmLabel={renderingModal?.gift.transferStars
|
|
||||||
? lang(
|
|
||||||
'GiftTransferConfirmButton',
|
|
||||||
{ amount: formatStarsAsIcon(lang, renderingModal.gift.transferStars, { asFont: true }) },
|
|
||||||
{ withNodes: true },
|
|
||||||
) : lang('GiftTransferConfirmButtonFree')}
|
|
||||||
confirmHandler={handleTransfer}
|
|
||||||
>
|
|
||||||
{renderingSelectedPeer && (
|
|
||||||
<GiftTransferPreview
|
|
||||||
peer={renderingSelectedPeer}
|
|
||||||
gift={uniqueGift}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<p>
|
|
||||||
{renderingModal?.gift.transferStars
|
|
||||||
? lang('GiftTransferConfirmDescription', {
|
|
||||||
gift: lang('GiftUnique', { title: uniqueGift.title, number: uniqueGift.number }),
|
|
||||||
amount: formatStarsAsText(lang, renderingModal.gift.transferStars),
|
|
||||||
peer: getPeerTitle(lang, renderingSelectedPeer!),
|
|
||||||
}, {
|
|
||||||
withNodes: true,
|
|
||||||
withMarkdown: true,
|
|
||||||
})
|
|
||||||
: lang('GiftTransferConfirmDescriptionFree', {
|
|
||||||
gift: lang('GiftUnique', { title: uniqueGift.title, number: uniqueGift.number }),
|
|
||||||
peer: getPeerTitle(lang, renderingSelectedPeer!),
|
|
||||||
}, {
|
|
||||||
withNodes: true,
|
|
||||||
withMarkdown: true,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</ConfirmDialog>
|
|
||||||
)}
|
|
||||||
</PickerModal>
|
</PickerModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import type { ApiSavedStarGift, ApiStarGiftUnique } from '../../../api/types';
|
import type { ApiSavedStarGift, ApiStarGiftUnique } from '../../../api/types';
|
||||||
import type { StarGiftCategory } from '../../../types';
|
|
||||||
import type { ActionReturnType } from '../../types';
|
import type { ActionReturnType } from '../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -8,7 +7,7 @@ import {
|
|||||||
TON_CURRENCY_CODE,
|
TON_CURRENCY_CODE,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByCallback, buildCollectionByKey } from '../../../util/iteratees';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import { RESALE_GIFTS_LIMIT } from '../../../limits';
|
import { RESALE_GIFTS_LIMIT } from '../../../limits';
|
||||||
import { areInputSavedGiftsEqual, getRequestInputSavedStarGift } from '../../helpers/payments';
|
import { areInputSavedGiftsEqual, getRequestInputSavedStarGift } from '../../helpers/payments';
|
||||||
@ -136,49 +135,83 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
|
|||||||
|
|
||||||
const byId = buildCollectionByKey(result.gifts, 'id');
|
const byId = buildCollectionByKey(result.gifts, 'id');
|
||||||
|
|
||||||
const idsByCategoryName: Record<StarGiftCategory, string[]> = {
|
|
||||||
all: [],
|
|
||||||
stock: [],
|
|
||||||
limited: [],
|
|
||||||
resale: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const allStarGiftIds = Object.keys(byId);
|
const allStarGiftIds = Object.keys(byId);
|
||||||
const allStarGifts = Object.values(byId);
|
const allStarGifts = Object.values(byId);
|
||||||
|
|
||||||
const limitedStarGiftIds = allStarGifts.map((gift) => (gift.isLimited ? gift.id : undefined))
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const stockedStarGiftIds = allStarGifts.map((gift) => (
|
|
||||||
gift.availabilityRemains || !gift.availabilityTotal ? gift.id : undefined
|
|
||||||
)).filter(Boolean);
|
|
||||||
|
|
||||||
const resaleStarGiftIds = allStarGifts.map((gift) => (gift.availabilityResale ? gift.id : undefined))
|
const resaleStarGiftIds = allStarGifts.map((gift) => (gift.availabilityResale ? gift.id : undefined))
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
idsByCategoryName.all = allStarGiftIds;
|
|
||||||
idsByCategoryName.limited = limitedStarGiftIds;
|
|
||||||
idsByCategoryName.stock = stockedStarGiftIds;
|
|
||||||
idsByCategoryName.resale = resaleStarGiftIds;
|
|
||||||
|
|
||||||
allStarGifts.forEach((gift) => {
|
|
||||||
const starsCategory = gift.stars;
|
|
||||||
if (!idsByCategoryName[starsCategory]) {
|
|
||||||
idsByCategoryName[starsCategory] = [];
|
|
||||||
}
|
|
||||||
idsByCategoryName[starsCategory].push(gift.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
global = {
|
global = {
|
||||||
...global,
|
...global,
|
||||||
starGifts: {
|
starGifts: {
|
||||||
byId,
|
byId,
|
||||||
idsByCategory: idsByCategoryName,
|
idsByCategory: {
|
||||||
|
all: allStarGiftIds,
|
||||||
|
resale: resaleStarGiftIds,
|
||||||
|
myCollectibles: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('loadMyCollectibleGifts', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { shouldRefresh } = payload || {};
|
||||||
|
const currentUserId = global.currentUserId;
|
||||||
|
if (!currentUserId) return;
|
||||||
|
|
||||||
|
const currentMyCollectibleGifts = global.myCollectibleGifts;
|
||||||
|
const localNextOffset = currentMyCollectibleGifts?.nextOffset;
|
||||||
|
|
||||||
|
if (currentMyCollectibleGifts && !localNextOffset && !shouldRefresh) return;
|
||||||
|
|
||||||
|
const peer = selectPeer(global, currentUserId);
|
||||||
|
if (!peer) return;
|
||||||
|
|
||||||
|
const result = await callApi('fetchSavedStarGifts', {
|
||||||
|
peer,
|
||||||
|
offset: !shouldRefresh ? localNextOffset : undefined,
|
||||||
|
filter: {
|
||||||
|
sortType: 'byDate',
|
||||||
|
shouldIncludeUnique: true,
|
||||||
|
shouldIncludeUnlimited: false,
|
||||||
|
shouldIncludeUpgradable: false,
|
||||||
|
shouldIncludeLimited: false,
|
||||||
|
shouldIncludeDisplayed: true,
|
||||||
|
shouldIncludeHidden: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
|
||||||
|
const gifts = result.gifts;
|
||||||
|
|
||||||
|
const byId = buildCollectionByCallback(gifts, (savedGift) => (
|
||||||
|
[savedGift.gift.id, savedGift]
|
||||||
|
));
|
||||||
|
|
||||||
|
const ids = gifts.map((gift) => gift.gift.id);
|
||||||
|
|
||||||
|
global = {
|
||||||
|
...global,
|
||||||
|
myCollectibleGifts: {
|
||||||
|
byId: {
|
||||||
|
...!shouldRefresh && (global.myCollectibleGifts?.byId || {}),
|
||||||
|
...byId,
|
||||||
|
},
|
||||||
|
ids: [
|
||||||
|
...!shouldRefresh ? (global.myCollectibleGifts?.ids || []) : [],
|
||||||
|
...ids,
|
||||||
|
],
|
||||||
|
nextOffset: result.nextOffset,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('updateResaleGiftsFilter', (global, actions, payload): ActionReturnType => {
|
addActionHandler('updateResaleGiftsFilter', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
filter, tabId = getCurrentTabId(),
|
filter, tabId = getCurrentTabId(),
|
||||||
@ -338,6 +371,9 @@ addActionHandler('reloadPeerSavedGifts', (global, actions, payload): ActionRetur
|
|||||||
actions.loadPeerSavedGifts({ peerId, shouldRefresh: true, tabId: tabState.id });
|
actions.loadPeerSavedGifts({ peerId, shouldRefresh: true, tabId: tabState.id });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (peerId === global.currentUserId) {
|
||||||
|
actions.loadMyCollectibleGifts({ shouldRefresh: true });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('loadStarsSubscriptions', async (global): Promise<void> => {
|
addActionHandler('loadStarsSubscriptions', async (global): Promise<void> => {
|
||||||
|
|||||||
@ -298,6 +298,8 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
global = updateTabState(global, {
|
global = updateTabState(global, {
|
||||||
isWaitingForStarGiftTransfer: undefined,
|
isWaitingForStarGiftTransfer: undefined,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
|
|
||||||
|
actions.reloadPeerSavedGifts({ peerId: global.currentUserId! });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -146,6 +146,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
},
|
},
|
||||||
tabId,
|
tabId,
|
||||||
});
|
});
|
||||||
|
actions.reloadPeerSavedGifts({ peerId: starGiftModalState.forPeerId });
|
||||||
actions.requestConfetti({ withStars: true, tabId });
|
actions.requestConfetti({ withStars: true, tabId });
|
||||||
actions.closeGiftModal({ tabId });
|
actions.closeGiftModal({ tabId });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -434,6 +434,21 @@ addActionHandler('openGiftTransferModal', (global, actions, payload): ActionRetu
|
|||||||
|
|
||||||
addTabStateResetterAction('closeGiftTransferModal', 'giftTransferModal');
|
addTabStateResetterAction('closeGiftTransferModal', 'giftTransferModal');
|
||||||
|
|
||||||
|
addActionHandler('openGiftTransferConfirmModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
gift, recipientId, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
giftTransferConfirmModal: {
|
||||||
|
gift,
|
||||||
|
recipientId,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeGiftTransferConfirmModal', 'giftTransferConfirmModal');
|
||||||
|
|
||||||
addActionHandler('updateSelectedGiftCollection', (global, actions, payload): ActionReturnType => {
|
addActionHandler('updateSelectedGiftCollection', (global, actions, payload): ActionReturnType => {
|
||||||
const { peerId, collectionId, tabId = getCurrentTabId() } = payload;
|
const { peerId, collectionId, tabId = getCurrentTabId() } = payload;
|
||||||
const tabState = selectTabState(global, tabId);
|
const tabState = selectTabState(global, tabId);
|
||||||
|
|||||||
@ -2544,6 +2544,9 @@ export interface ActionPayloads {
|
|||||||
loadPremiumGifts: undefined;
|
loadPremiumGifts: undefined;
|
||||||
loadTonGifts: undefined;
|
loadTonGifts: undefined;
|
||||||
loadStarGifts: undefined;
|
loadStarGifts: undefined;
|
||||||
|
loadMyCollectibleGifts: {
|
||||||
|
shouldRefresh?: true;
|
||||||
|
} | undefined;
|
||||||
updateResaleGiftsFilter: {
|
updateResaleGiftsFilter: {
|
||||||
filter: ResaleGiftsFilterOptions;
|
filter: ResaleGiftsFilterOptions;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
@ -2643,6 +2646,11 @@ export interface ActionPayloads {
|
|||||||
recipientId: string;
|
recipientId: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeGiftTransferModal: WithTabId | undefined;
|
closeGiftTransferModal: WithTabId | undefined;
|
||||||
|
openGiftTransferConfirmModal: {
|
||||||
|
gift: ApiSavedStarGift;
|
||||||
|
recipientId: string;
|
||||||
|
} & WithTabId;
|
||||||
|
closeGiftTransferConfirmModal: WithTabId | undefined;
|
||||||
updateSelectedGiftCollection: {
|
updateSelectedGiftCollection: {
|
||||||
peerId: string;
|
peerId: string;
|
||||||
collectionId: number;
|
collectionId: number;
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import type {
|
|||||||
ApiReaction,
|
ApiReaction,
|
||||||
ApiReactionKey,
|
ApiReactionKey,
|
||||||
ApiSavedReactionTag,
|
ApiSavedReactionTag,
|
||||||
|
ApiSavedStarGift,
|
||||||
ApiSession,
|
ApiSession,
|
||||||
ApiSponsoredMessage,
|
ApiSponsoredMessage,
|
||||||
ApiStarGiftCollection,
|
ApiStarGiftCollection,
|
||||||
@ -311,6 +312,11 @@ export type GlobalState = {
|
|||||||
byId: Record<string, ApiStarGiftRegular>;
|
byId: Record<string, ApiStarGiftRegular>;
|
||||||
idsByCategory: Record<StarGiftCategory, string[]>;
|
idsByCategory: Record<StarGiftCategory, string[]>;
|
||||||
};
|
};
|
||||||
|
myCollectibleGifts?: {
|
||||||
|
byId: Record<string, ApiSavedStarGift>;
|
||||||
|
ids: string[];
|
||||||
|
nextOffset?: string;
|
||||||
|
};
|
||||||
starGiftCollections?: {
|
starGiftCollections?: {
|
||||||
byPeerId: Record<string, ApiStarGiftCollection[]>;
|
byPeerId: Record<string, ApiStarGiftCollection[]>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -849,6 +849,11 @@ export type TabState = {
|
|||||||
gift: ApiSavedStarGift;
|
gift: ApiSavedStarGift;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
giftTransferConfirmModal?: {
|
||||||
|
gift: ApiSavedStarGift;
|
||||||
|
recipientId: string;
|
||||||
|
};
|
||||||
|
|
||||||
giftUpgradeModal?: {
|
giftUpgradeModal?: {
|
||||||
sampleAttributes: ApiStarGiftAttribute[];
|
sampleAttributes: ApiStarGiftAttribute[];
|
||||||
recipientId?: string;
|
recipientId?: string;
|
||||||
|
|||||||
@ -669,7 +669,7 @@ export type WebPageMediaSize = 'large' | 'small';
|
|||||||
|
|
||||||
export type AttachmentCompression = 'compress' | 'original';
|
export type AttachmentCompression = 'compress' | 'original';
|
||||||
|
|
||||||
export type StarGiftCategory = number | 'all' | 'limited' | 'stock' | 'resale';
|
export type StarGiftCategory = 'all' | 'myCollectibles' | 'resale';
|
||||||
|
|
||||||
export type CallSound = (
|
export type CallSound = (
|
||||||
'join' | 'allowTalk' | 'leave' | 'connecting' | 'incoming' | 'end' | 'connect' | 'busy' | 'ringing'
|
'join' | 'allowTalk' | 'leave' | 'connecting' | 'incoming' | 'end' | 'connect' | 'busy' | 'ringing'
|
||||||
|
|||||||
7
src/types/language.d.ts
vendored
7
src/types/language.d.ts
vendored
@ -1232,6 +1232,7 @@ export interface LangPair {
|
|||||||
'GiftPremiumDescriptionLink': undefined;
|
'GiftPremiumDescriptionLink': undefined;
|
||||||
'StarsGiftHeader': undefined;
|
'StarsGiftHeader': undefined;
|
||||||
'StarsGiftHeaderSelf': undefined;
|
'StarsGiftHeaderSelf': undefined;
|
||||||
|
'StarGiftDescriptionCollectibles': undefined;
|
||||||
'StarGiftDescriptionSelf': undefined;
|
'StarGiftDescriptionSelf': undefined;
|
||||||
'GiftLimited': undefined;
|
'GiftLimited': undefined;
|
||||||
'GiftSoldOut': undefined;
|
'GiftSoldOut': undefined;
|
||||||
@ -1304,8 +1305,6 @@ export interface LangPair {
|
|||||||
'GiftWithdrawTitle': undefined;
|
'GiftWithdrawTitle': undefined;
|
||||||
'GiftWithdrawSubmit': undefined;
|
'GiftWithdrawSubmit': undefined;
|
||||||
'AllGiftsCategory': undefined;
|
'AllGiftsCategory': undefined;
|
||||||
'LimitedGiftsCategory': undefined;
|
|
||||||
'StockGiftsCategory': undefined;
|
|
||||||
'PremiumGiftDescription': undefined;
|
'PremiumGiftDescription': undefined;
|
||||||
'StarsReactionLinkText': undefined;
|
'StarsReactionLinkText': undefined;
|
||||||
'StarsReactionLink': undefined;
|
'StarsReactionLink': undefined;
|
||||||
@ -1578,7 +1577,8 @@ export interface LangPair {
|
|||||||
'StarGiftPurchaseTransaction': undefined;
|
'StarGiftPurchaseTransaction': undefined;
|
||||||
'ContextMenuItemMention': undefined;
|
'ContextMenuItemMention': undefined;
|
||||||
'GiftRibbonResale': undefined;
|
'GiftRibbonResale': undefined;
|
||||||
'GiftCategoryResale': undefined;
|
'GiftCategoryCollectibles': undefined;
|
||||||
|
'GiftCategoryMyGifts': undefined;
|
||||||
'GiftSortByPrice': undefined;
|
'GiftSortByPrice': undefined;
|
||||||
'GiftSortByNumber': undefined;
|
'GiftSortByNumber': undefined;
|
||||||
'ContextMenuItemSelectAll': undefined;
|
'ContextMenuItemSelectAll': undefined;
|
||||||
@ -1709,6 +1709,7 @@ export interface LangPair {
|
|||||||
'GiftValueForSaleOnFragment': undefined;
|
'GiftValueForSaleOnFragment': undefined;
|
||||||
'GiftValueForSaleOnTelegram': undefined;
|
'GiftValueForSaleOnTelegram': undefined;
|
||||||
'EmbeddedMessageNoCaption': undefined;
|
'EmbeddedMessageNoCaption': undefined;
|
||||||
|
'ConfirmBuyGiftForTonDescription': undefined;
|
||||||
'TitleGiftLocked': undefined;
|
'TitleGiftLocked': undefined;
|
||||||
'QuickPreview': undefined;
|
'QuickPreview': undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user