diff --git a/src/api/gramjs/apiBuilders/gifts.ts b/src/api/gramjs/apiBuilders/gifts.ts index 39c39e2a2..587909afb 100644 --- a/src/api/gramjs/apiBuilders/gifts.ts +++ b/src/api/gramjs/apiBuilders/gifts.ts @@ -158,7 +158,7 @@ export function buildApiStarGiftAttribute(attribute: GramJs.TypeStarGiftAttribut export function buildApiSavedStarGift(userStarGift: GramJs.SavedStarGift, peerId: string): ApiSavedStarGift { const { gift, date, convertStars, fromId, message, msgId, nameHidden, unsaved, upgradeStars, transferStars, canUpgrade, - savedId, canExportAt, pinnedToTop, canResellAt, canTransferAt, prepaidUpgradeHash, + savedId, canExportAt, pinnedToTop, canResellAt, canTransferAt, prepaidUpgradeHash, dropOriginalDetailsStars, } = userStarGift; const inputGift: ApiInputSavedStarGift | undefined = savedId && peerId @@ -183,6 +183,7 @@ export function buildApiSavedStarGift(userStarGift: GramJs.SavedStarGift, peerId canResellAt, canTransferAt, isPinned: pinnedToTop, + dropOriginalDetailsStars: dropOriginalDetailsStars !== undefined ? toJSNumber(dropOriginalDetailsStars) : undefined, prepaidUpgradeHash, }; } diff --git a/src/api/gramjs/apiBuilders/messageActions.ts b/src/api/gramjs/apiBuilders/messageActions.ts index 946b1099c..4b3d36a6a 100644 --- a/src/api/gramjs/apiBuilders/messageActions.ts +++ b/src/api/gramjs/apiBuilders/messageActions.ts @@ -422,7 +422,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess if (action instanceof GramJs.MessageActionStarGiftUnique) { const { upgrade, transferred, saved, refunded, gift, canExportAt, transferStars, fromId, peer, savedId, - resaleAmount, prepaidUpgrade, + resaleAmount, prepaidUpgrade, dropOriginalDetailsStars, } = action; const starGift = buildApiStarGift(gift); @@ -443,6 +443,9 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess peerId: peer && getApiChatIdFromMtpPeer(peer), savedId: savedId !== undefined ? buildApiPeerId(savedId, 'user') : undefined, resaleAmount: resaleAmount ? buildApiCurrencyAmount(resaleAmount) : undefined, + dropOriginalDetailsStars: dropOriginalDetailsStars !== undefined + ? toJSNumber(dropOriginalDetailsStars) + : undefined, }; } if (action instanceof GramJs.MessageActionPaidMessagesPrice) { diff --git a/src/api/gramjs/apiBuilders/payments.ts b/src/api/gramjs/apiBuilders/payments.ts index ff8a2937f..e0151077f 100644 --- a/src/api/gramjs/apiBuilders/payments.ts +++ b/src/api/gramjs/apiBuilders/payments.ts @@ -553,7 +553,7 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): const { date, id, peer, amount, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction, subscriptionPeriod, stargift, giveawayPostId, starrefCommissionPermille, stargiftUpgrade, paidMessages, - stargiftResale, postsSearch, stargiftPrepaidUpgrade, + stargiftResale, postsSearch, stargiftPrepaidUpgrade, stargiftDropOriginalDetails, } = transaction; if (photo) { @@ -593,6 +593,7 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): isGiftResale: stargiftResale, paidMessages, isPostsSearch: postsSearch, + isDropOriginalDetails: stargiftDropOriginalDetails, isPrepaidUpgrade: stargiftPrepaidUpgrade, }; } diff --git a/src/api/gramjs/gramjsBuilders/index.ts b/src/api/gramjs/gramjsBuilders/index.ts index 394dca363..34a0bac83 100644 --- a/src/api/gramjs/gramjsBuilders/index.ts +++ b/src/api/gramjs/gramjsBuilders/index.ts @@ -781,6 +781,12 @@ export function buildInputInvoice(invoice: ApiRequestInputInvoice) { }); } + case 'stargiftDropOriginalDetails': { + return new GramJs.InputInvoiceStarGiftDropOriginalDetails({ + stargift: buildInputSavedStarGift(invoice.inputSavedGift), + }); + } + case 'stargiftPrepaidUpgrade': { return new GramJs.InputInvoiceStarGiftPrepaidUpgrade({ peer: buildInputPeer(invoice.peer.id, invoice.peer.accessHash), diff --git a/src/api/types/messageActions.ts b/src/api/types/messageActions.ts index 42d904260..a8a3cd983 100644 --- a/src/api/types/messageActions.ts +++ b/src/api/types/messageActions.ts @@ -269,6 +269,7 @@ export interface ApiMessageActionStarGiftUnique extends ActionMediaType { peerId?: string; savedId?: string; resaleAmount?: ApiTypeCurrencyAmount; + dropOriginalDetailsStars?: number; } export interface ApiMessageActionChannelJoined extends ActionMediaType { diff --git a/src/api/types/payments.ts b/src/api/types/payments.ts index 7a909ef4c..0969dd49f 100644 --- a/src/api/types/payments.ts +++ b/src/api/types/payments.ts @@ -399,6 +399,11 @@ export type ApiInputInvoiceStarGiftTransfer = { recipientId: string; }; +export type ApiInputInvoiceStarGiftDropOriginalDetails = { + type: 'stargiftDropOriginalDetails'; + inputSavedGift: ApiInputSavedStarGift; +}; + export type ApiInputInvoiceStarGiftPrepaidUpgrade = { type: 'stargiftPrepaidUpgrade'; peerId: string; @@ -409,7 +414,7 @@ export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | Api | ApiInputInvoiceGiftCode | ApiInputInvoicePremiumGiftStars | ApiInputInvoiceStars | ApiInputInvoiceStarsGift | ApiInputInvoiceStarsGiveaway | ApiInputInvoiceStarGift | ApiInputInvoiceChatInviteSubscription | ApiInputInvoiceStarGiftUpgrade | ApiInputInvoiceStarGiftTransfer | ApiInputInvoiceStarGiftResale - | ApiInputInvoiceStarGiftPrepaidUpgrade; + | ApiInputInvoiceStarGiftDropOriginalDetails | ApiInputInvoiceStarGiftPrepaidUpgrade; /* Used for Invoice request */ export type ApiRequestInputInvoiceMessage = { @@ -479,6 +484,11 @@ export type ApiRequestInputInvoiceStarGiftTransfer = { recipient: ApiPeer; }; +export type ApiRequestInputInvoiceStarGiftDropOriginalDetails = { + type: 'stargiftDropOriginalDetails'; + inputSavedGift: ApiRequestInputSavedStarGift; +}; + export type ApiRequestInputInvoiceStarGiftPrepaidUpgrade = { type: 'stargiftPrepaidUpgrade'; peer: ApiPeer; @@ -490,7 +500,7 @@ export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestI | ApiRequestInputInvoiceChatInviteSubscription | ApiRequestInputInvoiceStarGift | ApiRequestInputInvoiceStarGiftUpgrade | ApiRequestInputInvoiceStarGiftTransfer | ApiRequestInputInvoicePremiumGiftStars | ApiRequestInputInvoiceStarGiftResale - | ApiRequestInputInvoiceStarGiftPrepaidUpgrade; + | ApiRequestInputInvoiceStarGiftDropOriginalDetails | ApiRequestInputInvoiceStarGiftPrepaidUpgrade; export interface ApiUniqueStarGiftValueInfo { isLastSaleOnFragment?: true; diff --git a/src/api/types/stars.ts b/src/api/types/stars.ts index 7a9c4ab44..480b80823 100644 --- a/src/api/types/stars.ts +++ b/src/api/types/stars.ts @@ -111,6 +111,7 @@ export interface ApiSavedStarGift { isConverted?: boolean; // Local field, used for Action Message upgradeMsgId?: number; // Local field, used for Action Message localTag?: number; // Local field, used for key in list + dropOriginalDetailsStars?: number; } export type StarGiftAttributeIdModel = { @@ -245,6 +246,7 @@ export interface ApiStarsTransaction { isGiftResale?: true; paidMessages?: number; isPostsSearch?: true; + isDropOriginalDetails?: true; isPrepaidUpgrade?: true; } diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index 82dfc85de..8bab9584d 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -1558,6 +1558,10 @@ "GiftTransferConfirmButton" = "Transfer for {amount}"; "GiftTransferConfirmButtonFree" = "Transfer"; "GiftTransferSuccessMessage" = "You have successfully gifted {gift} to {peer}."; +"RemoveGiftDescriptionTitle" = "Remove Description"; +"RemoveGiftDescriptionConfirmText" = "Do you want to permanently remove this description from your gift?"; +"RemoveGiftDescriptionButton" = "Remove for {amount}"; +"RemoveGiftDescriptionSuccessMessage" = "Gift's Description Removed!"; "GiftUpgradeUniqueTitle" = "Unique"; "GiftUpgradeUniqueDescription" = "Turn your gift into a unique collectible that you can transfer or auction."; "GiftUpgradeTransferableTitle" = "Transferable"; @@ -2301,6 +2305,8 @@ "TitleGiftLocked" = "Gift Locked"; "GiftLockedMessage" = "This gift is currently only available to earlier Telegram users. It will unlock for your account in about **{relativeDate}**."; "QuickPreview" = "Quick Preview"; +"DropOriginalDetailsTransaction" = "Removed Gift Description"; +"StarGiftReasonDropOriginalDetails" = "Removed Description"; "GiftAnUpgradeButton" = "Gift an Upgrade"; "GiftPrepaidUpgradeTransactionTitle" = "Gift Upgrade"; "ActionStarGiftPrepaidUpgraded" = "{user} turned the gift into a unique collectible"; diff --git a/src/bundles/stars.ts b/src/bundles/stars.ts index 2e0b11c44..74b093bb2 100644 --- a/src/bundles/stars.ts +++ b/src/bundles/stars.ts @@ -16,5 +16,6 @@ export { default as GiftStatusInfoModal } from '../components/modals/gift/status export { default as GiftWithdrawModal } from '../components/modals/gift/withdraw/GiftWithdrawModal'; export { default as GiftTransferModal } from '../components/modals/gift/transfer/GiftTransferModal'; export { default as GiftTransferConfirmModal } from '../components/modals/gift/transfer/GiftTransferConfirmModal'; +export { default as GiftDescriptionRemoveModal } from '../components/modals/gift/message/GiftDescriptionRemoveModal'; export { default as ChatRefundModal } from '../components/modals/stars/chatRefund/ChatRefundModal'; export { default as PriceConfirmModal } from '../components/modals/priceConfirm/PriceConfirmModal'; diff --git a/src/components/common/helpers/giftOriginalInfo.tsx b/src/components/common/helpers/giftOriginalInfo.tsx new file mode 100644 index 000000000..184ac60a9 --- /dev/null +++ b/src/components/common/helpers/giftOriginalInfo.tsx @@ -0,0 +1,75 @@ +import type { TeactNode } from '../../../lib/teact/teact'; + +import type { ApiPeer, ApiStarGiftAttributeOriginalDetails } from '../../../api/types'; + +import { getPeerTitle } from '../../../global/helpers/peers'; +import { formatDateTimeToString } from '../../../util/dates/dateFormat'; +import { type LangFn } from '../../../util/localization'; +import { renderTextWithEntities } from './renderTextWithEntities'; + +import Link from '../../ui/Link'; + +type GiftOriginalInfoOptions = { + originalDetails: ApiStarGiftAttributeOriginalDetails; + recipient: ApiPeer; + sender?: ApiPeer; + onOpenChat: (peerId: string) => void; + lang: LangFn; +}; + +export function renderGiftOriginalInfo({ + originalDetails, + recipient, + sender, + onOpenChat, + lang, +}: GiftOriginalInfoOptions): TeactNode | undefined { + const { recipientId, senderId, date, message } = originalDetails; + + const formattedDate = formatDateTimeToString(date * 1000, lang.code, true); + const recipientLink = ( + onOpenChat(recipientId)} isPrimary> + {getPeerTitle(lang, recipient)} + + ); + + if (!sender || senderId === recipientId) { + return message + ? lang('GiftInfoPeerOriginalInfoText', { + peer: recipientLink, + text: renderTextWithEntities(message), + date: formattedDate, + }, { + withNodes: true, + }) + : lang('GiftInfoPeerOriginalInfo', { + peer: recipientLink, + date: formattedDate, + }, { + withNodes: true, + }); + } + + const senderLink = ( + onOpenChat(sender.id)} isPrimary> + {getPeerTitle(lang, sender)} + + ); + + return message + ? lang('GiftInfoPeerOriginalInfoTextSender', { + peer: recipientLink, + sender: senderLink, + text: renderTextWithEntities(message), + date: formattedDate, + }, { + withNodes: true, + }) + : lang('GiftInfoPeerOriginalInfoSender', { + peer: recipientLink, + date: formattedDate, + sender: senderLink, + }, { + withNodes: true, + }); +} diff --git a/src/components/modals/ModalContainer.tsx b/src/components/modals/ModalContainer.tsx index ceb182f0c..a40043d73 100644 --- a/src/components/modals/ModalContainer.tsx +++ b/src/components/modals/ModalContainer.tsx @@ -22,6 +22,7 @@ import FrozenAccountModal from './frozenAccount/FrozenAccountModal.async'; import PremiumGiftModal from './gift/GiftModal.async'; import GiftInfoModal from './gift/info/GiftInfoModal.async'; import GiftLockedModal from './gift/locked/GiftLockedModal.async'; +import GiftDescriptionRemoveModal from './gift/message/GiftDescriptionRemoveModal.async'; import GiftRecipientPicker from './gift/recipient/GiftRecipientPicker.async'; import GiftResalePriceComposerModal from './gift/resale/GiftResalePriceComposerModal.async'; import GiftStatusInfoModal from './gift/status/GiftStatusInfoModal.async'; @@ -97,6 +98,7 @@ type ModalKey = keyof Pick { + if (!savedGift?.inputGift || !savedGift.dropOriginalDetailsStars || !gift || !giftAttributes) return; + + const { originalDetails } = giftAttributes; + if (!originalDetails) return; + + openGiftDescriptionRemoveModal({ + gift: savedGift, + price: savedGift.dropOriginalDetailsStars, + details: originalDetails, + }); + }); + const handleOpenUpgradeModal = useLastCallback(() => { if (!savedGift) return; const giftOwnerId = renderingTargetPeer?.id; @@ -761,9 +775,7 @@ const GiftInfoModal = ({ } if (originalDetails) { - const { - date, recipientId, message, senderId, - } = originalDetails; + const { recipientId, senderId } = originalDetails; const global = getGlobal(); // Peer titles do not need to be reactive const openChat = (id: string) => { @@ -774,54 +786,29 @@ const GiftInfoModal = ({ const recipient = selectPeer(global, recipientId)!; const sender = senderId ? selectPeer(global, senderId) : undefined; - const formattedDate = formatDateTimeToString(date * 1000, lang.code, true); - const recipientLink = ( - - openChat(recipientId)} isPrimary> - {getPeerTitle(lang, recipient)} - - ); - - let text: TeactNode | undefined; - if (!sender || senderId === recipientId) { - text = message ? lang('GiftInfoPeerOriginalInfoText', { - peer: recipientLink, - text: renderTextWithEntities(message), - date: formattedDate, - }, { - withNodes: true, - }) : lang('GiftInfoPeerOriginalInfo', { - peer: recipientLink, - date: formattedDate, - }, { - withNodes: true, - }); - } else { - const senderLink = ( - - openChat(sender.id)} isPrimary> - {getPeerTitle(lang, sender)} - - ); - text = message ? lang('GiftInfoPeerOriginalInfoTextSender', { - peer: recipientLink, - sender: senderLink, - text: renderTextWithEntities(message), - date: formattedDate, - }, { - withNodes: true, - }) : lang('GiftInfoPeerOriginalInfoSender', { - peer: recipientLink, - date: formattedDate, - sender: senderLink, - }, { - withNodes: true, - }); - } + const text = renderGiftOriginalInfo({ + originalDetails, recipient, sender, onOpenChat: openChat, lang, + }); tableData.push([ undefined, - {text}, +
+
+ {text} +
+ {Boolean(savedGift?.dropOriginalDetailsStars) && ( + + )} +
, ]); } } diff --git a/src/components/modals/gift/message/GiftDescriptionRemoveModal.async.tsx b/src/components/modals/gift/message/GiftDescriptionRemoveModal.async.tsx new file mode 100644 index 000000000..7152ba9fc --- /dev/null +++ b/src/components/modals/gift/message/GiftDescriptionRemoveModal.async.tsx @@ -0,0 +1,16 @@ +import type { FC } from '../../../../lib/teact/teact'; + +import type { OwnProps } from './GiftDescriptionRemoveModal'; + +import { Bundles } from '../../../../util/moduleLoader'; + +import useModuleLoader from '../../../../hooks/useModuleLoader'; + +const GiftDescriptionRemoveModalAsync: FC = (props) => { + const { modal } = props; + const GiftDescriptionRemoveModal = useModuleLoader(Bundles.Stars, 'GiftDescriptionRemoveModal', !modal); + + return GiftDescriptionRemoveModal ? : undefined; +}; + +export default GiftDescriptionRemoveModalAsync; diff --git a/src/components/modals/gift/message/GiftDescriptionRemoveModal.module.scss b/src/components/modals/gift/message/GiftDescriptionRemoveModal.module.scss new file mode 100644 index 000000000..65d68f30b --- /dev/null +++ b/src/components/modals/gift/message/GiftDescriptionRemoveModal.module.scss @@ -0,0 +1,14 @@ +.confirmText { + margin-bottom: 1.5rem; +} + +.giftDescription { + margin-bottom: 1rem; + padding: 0.375rem; + border: 0.0625rem solid var(--color-borders); + border-radius: 0.25rem; + + text-align: center; + + background-color: var(--color-background-secondary); +} diff --git a/src/components/modals/gift/message/GiftDescriptionRemoveModal.tsx b/src/components/modals/gift/message/GiftDescriptionRemoveModal.tsx new file mode 100644 index 000000000..1a8c189df --- /dev/null +++ b/src/components/modals/gift/message/GiftDescriptionRemoveModal.tsx @@ -0,0 +1,94 @@ +import { memo } from '../../../../lib/teact/teact'; +import { getActions, withGlobal } from '../../../../global'; + +import type { ApiPeer } from '../../../../api/types'; +import type { TabState } from '../../../../global/types'; + +import { selectPeer } from '../../../../global/selectors'; +import { formatStarsAsIcon } from '../../../../util/localization/format'; +import { renderGiftOriginalInfo } from '../../../common/helpers/giftOriginalInfo'; + +import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev'; +import useLang from '../../../../hooks/useLang'; +import useLastCallback from '../../../../hooks/useLastCallback'; + +import ConfirmDialog from '../../../ui/ConfirmDialog'; + +import styles from './GiftDescriptionRemoveModal.module.scss'; + +export type OwnProps = { + modal: TabState['giftDescriptionRemoveModal']; +}; + +type StateProps = { + senderPeer?: ApiPeer; + recipientPeer?: ApiPeer; +}; + +const GiftDescriptionRemoveModal = ({ + modal, senderPeer, recipientPeer, +}: OwnProps & StateProps) => { + const { + closeGiftDescriptionRemoveModal, removeGiftDescription, openChat, + } = getActions(); + const lang = useLang(); + + const isOpen = Boolean(modal); + const renderingModal = useCurrentOrPrev(modal); + const renderingSenderPeer = useCurrentOrPrev(senderPeer); + const renderingRecipientPeer = useCurrentOrPrev(recipientPeer); + + const openChatHandler = useLastCallback((id: string) => { + closeGiftDescriptionRemoveModal(); + openChat({ id }); + }); + + const handleConfirm = useLastCallback(() => { + if (!renderingModal?.gift.inputGift || !renderingModal.price) return; + + removeGiftDescription({ + gift: renderingModal.gift.inputGift, + price: renderingModal.price, + }); + }); + + if (!renderingModal || !renderingRecipientPeer) return undefined; + + const { price, details } = renderingModal; + + const description = renderGiftOriginalInfo({ + originalDetails: details, recipient: renderingRecipientPeer, + sender: renderingSenderPeer, onOpenChat: openChatHandler, lang, + }); + + return ( + +
{lang('RemoveGiftDescriptionConfirmText')}
+ {Boolean(description) && ( +
+ {description} +
+ )} +
+ ); +}; + +export default memo( + withGlobal((global, { modal }): Complete => { + const senderPeer = modal?.details.senderId ? selectPeer(global, modal.details.senderId) : undefined; + const recipientPeer = modal?.details.recipientId ? selectPeer(global, modal.details.recipientId) : undefined; + + return { + senderPeer, + recipientPeer, + }; + })(GiftDescriptionRemoveModal), +); diff --git a/src/components/modals/stars/helpers/transaction.ts b/src/components/modals/stars/helpers/transaction.ts index ad7863b9b..efaaa99e6 100644 --- a/src/components/modals/stars/helpers/transaction.ts +++ b/src/components/modals/stars/helpers/transaction.ts @@ -29,6 +29,10 @@ export function getTransactionTitle(oldLang: OldLangFn, lang: LangFn, transactio return lang('PostsSearchTransaction'); } + if (transaction.isDropOriginalDetails) { + return lang('DropOriginalDetailsTransaction'); + } + if (transaction.isPrepaidUpgrade) { return lang('GiftPrepaidUpgradeTransactionTitle'); } diff --git a/src/components/modals/stars/transaction/StarsTransactionItem.tsx b/src/components/modals/stars/transaction/StarsTransactionItem.tsx index 567df7cd9..22daf675e 100644 --- a/src/components/modals/stars/transaction/StarsTransactionItem.tsx +++ b/src/components/modals/stars/transaction/StarsTransactionItem.tsx @@ -83,7 +83,8 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => { avatarPeer = customPeer; } - if (transaction.isGiftUpgrade && transaction.starGift?.type === 'starGiftUnique') { + if ((transaction.isGiftUpgrade || transaction.isDropOriginalDetails) + && transaction.starGift?.type === 'starGiftUnique') { description = lang('GiftUnique', { title: transaction.starGift.title, number: transaction.starGift.number }); } diff --git a/src/components/modals/stars/transaction/StarsTransactionModal.tsx b/src/components/modals/stars/transaction/StarsTransactionModal.tsx index e4bd655b9..a74b1b556 100644 --- a/src/components/modals/stars/transaction/StarsTransactionModal.tsx +++ b/src/components/modals/stars/transaction/StarsTransactionModal.tsx @@ -98,7 +98,7 @@ const StarsTransactionModal: FC = ({ } const { - giveawayPostId, photo, amount, isGiftUpgrade, starGift, isGiftResale, + giveawayPostId, photo, amount, isGiftUpgrade, isDropOriginalDetails, starGift, isGiftResale, starRefCommision, } = transaction; @@ -116,7 +116,7 @@ const StarsTransactionModal: FC = ({ const title = getTransactionTitle(oldLang, lang, transaction); - const messageLink = peer && transaction.messageId && !isGiftUpgrade + const messageLink = peer && transaction.messageId && !isGiftUpgrade && !isDropOriginalDetails ? getMessageLink(peer, undefined, transaction.messageId) : undefined; const giveawayMessageLink = peer && giveawayPostId ? getMessageLink(peer, undefined, giveawayPostId) : undefined; @@ -131,7 +131,7 @@ const StarsTransactionModal: FC = ({ : oldLang('Media', mediaAmount); const description = transaction.description - || (isGiftUpgrade && starGift?.type === 'starGiftUnique' ? starGift.title : undefined) + || ((isGiftUpgrade || isDropOriginalDetails) && starGift?.type === 'starGiftUnique' ? starGift.title : undefined) || (media ? mediaText : undefined); const shouldDisplayAvatar = !media && !sticker && !transaction.isPostsSearch; @@ -231,6 +231,13 @@ const StarsTransactionModal: FC = ({ ]); } + if (isDropOriginalDetails) { + tableData.push([ + oldLang('StarGiftReason'), + lang('StarGiftReasonDropOriginalDetails'), + ]); + } + if (isGiftResale) { tableData.push([ oldLang('StarGiftReason'), @@ -253,7 +260,7 @@ const StarsTransactionModal: FC = ({ peerLabel = oldLang('Stars.Transaction.Via'); } - if (!transaction.isPostsSearch) { + if (!transaction.isPostsSearch && !isDropOriginalDetails) { tableData.push([ peerLabel, peerId ? { chatId: peerId } : toName || '', diff --git a/src/global/actions/api/payments.ts b/src/global/actions/api/payments.ts index b9a8df970..fe17f540b 100644 --- a/src/global/actions/api/payments.ts +++ b/src/global/actions/api/payments.ts @@ -1117,6 +1117,17 @@ addActionHandler('transferGift', (global, actions, payload): ActionReturnType => payInputStarInvoice(global, invoice, transferStars, tabId); }); +addActionHandler('removeGiftDescription', (global, actions, payload): ActionReturnType => { + const { gift, price, tabId = getCurrentTabId() } = payload; + + const invoice: ApiInputInvoice = { + type: 'stargiftDropOriginalDetails', + inputSavedGift: gift, + }; + + payInputStarInvoice(global, invoice, price, tabId); +}); + addActionHandler('upgradePrepaidGift', (global, actions, payload): ActionReturnType => { const { peerId, hash, stars, tabId = getCurrentTabId() } = payload; diff --git a/src/global/actions/apiUpdaters/payments.ts b/src/global/actions/apiUpdaters/payments.ts index 4faf277ba..3a2565581 100644 --- a/src/global/actions/apiUpdaters/payments.ts +++ b/src/global/actions/apiUpdaters/payments.ts @@ -3,8 +3,8 @@ import type { ActionReturnType } from '../../types'; import { formatCurrencyAsString } from '../../../util/formatCurrency'; import * as langProvider from '../../../util/oldLangProvider'; import { getPeerTitle } from '../../helpers/peers'; -import { addActionHandler, setGlobal } from '../../index'; -import { updateStarsBalance } from '../../reducers'; +import { addActionHandler, getGlobal, setGlobal } from '../../index'; +import { removeGiftInfoOriginalDetails, updateStarsBalance } from '../../reducers'; import { updateTabState } from '../../reducers/tabs'; import { selectPeer, selectTabState } from '../../selectors'; @@ -176,6 +176,22 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { actions.reloadPeerSavedGifts({ peerId: global.currentUserId }); } + if (inputInvoice?.type === 'stargiftDropOriginalDetails') { + global = getGlobal(); + global = removeGiftInfoOriginalDetails(global, tabId); + setGlobal(global); + + actions.closeGiftDescriptionRemoveModal({ tabId }); + actions.showNotification({ + message: { key: 'RemoveGiftDescriptionSuccessMessage' }, + tabId, + }); + + if (global.currentUserId) { + actions.reloadPeerSavedGifts({ peerId: global.currentUserId }); + } + } + if (inputInvoice?.type === 'stargiftPrepaidUpgrade') { actions.reloadPeerSavedGifts({ peerId: inputInvoice.peerId }); diff --git a/src/global/actions/ui/stars.ts b/src/global/actions/ui/stars.ts index d6dc77983..35ac3825b 100644 --- a/src/global/actions/ui/stars.ts +++ b/src/global/actions/ui/stars.ts @@ -266,6 +266,7 @@ addActionHandler('openGiftInfoModalFromMessage', async (global, actions, payload canExportAt: uniqueGift?.canExportAt, savedId: action.savedId, transferStars: uniqueGift?.transferStars, + dropOriginalDetailsStars: uniqueGift?.dropOriginalDetailsStars, prepaidUpgradeHash: starGift?.prepaidUpgradeHash, }; @@ -457,6 +458,22 @@ addActionHandler('openGiftTransferConfirmModal', (global, actions, payload): Act addTabStateResetterAction('closeGiftTransferConfirmModal', 'giftTransferConfirmModal'); +addActionHandler('openGiftDescriptionRemoveModal', (global, actions, payload): ActionReturnType => { + const { + gift, price, details, tabId = getCurrentTabId(), + } = payload; + + return updateTabState(global, { + giftDescriptionRemoveModal: { + gift, + price, + details, + }, + }, tabId); +}); + +addTabStateResetterAction('closeGiftDescriptionRemoveModal', 'giftDescriptionRemoveModal'); + addActionHandler('updateSelectedGiftCollection', (global, actions, payload): ActionReturnType => { const { peerId, collectionId, tabId = getCurrentTabId() } = payload; const tabState = selectTabState(global, tabId); diff --git a/src/global/helpers/payments.ts b/src/global/helpers/payments.ts index 1a34ceab1..ba8c5d3b7 100644 --- a/src/global/helpers/payments.ts +++ b/src/global/helpers/payments.ts @@ -235,6 +235,17 @@ export function getRequestInputInvoice( }; } + if (inputInvoice.type === 'stargiftDropOriginalDetails') { + const { inputSavedGift } = inputInvoice; + const savedGift = getRequestInputSavedStarGift(global, inputSavedGift); + if (!savedGift) return undefined; + + return { + type: 'stargiftDropOriginalDetails', + inputSavedGift: savedGift, + }; + } + if (inputInvoice.type === 'stargiftPrepaidUpgrade') { const { peerId, hash } = inputInvoice; const peer = selectPeer(global, peerId); diff --git a/src/global/reducers/gifts.ts b/src/global/reducers/gifts.ts new file mode 100644 index 000000000..928160efe --- /dev/null +++ b/src/global/reducers/gifts.ts @@ -0,0 +1,48 @@ +import type { ApiSavedStarGift } from '../../api/types'; +import type { GlobalState } from '../types'; + +import { getCurrentTabId } from '../../util/establishMultitabRole'; +import { updateTabState } from './tabs'; + +export function removeGiftInfoOriginalDetails( + global: T, + tabId: number = getCurrentTabId(), +): T { + const tabState = global.byTabId[tabId]; + const { giftInfoModal } = tabState; + if (!giftInfoModal) { + return global; + } + + const typeGift = giftInfoModal.gift; + const isSavedGift = typeGift && 'gift' in typeGift; + + if (!isSavedGift) { + return global; + } + + const savedGift = typeGift; + const innerGift = savedGift.gift; + + if (innerGift.type !== 'starGiftUnique') { + return global; + } + + const updatedInnerGift = { + ...innerGift, + attributes: innerGift.attributes?.filter((attr) => attr.type !== 'originalDetails'), + }; + + const updatedGift: ApiSavedStarGift = { + ...savedGift, + dropOriginalDetailsStars: undefined, + gift: updatedInnerGift, + }; + + return updateTabState(global, { + giftInfoModal: { + ...giftInfoModal, + gift: updatedGift, + }, + }, tabId); +} diff --git a/src/global/reducers/index.ts b/src/global/reducers/index.ts index 99507b0fd..261898931 100644 --- a/src/global/reducers/index.ts +++ b/src/global/reducers/index.ts @@ -14,3 +14,4 @@ export * from './stories'; export * from './translations'; export * from './peers'; export * from './topics'; +export * from './gifts'; diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index 8a1d11883..a5387fa1d 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -45,6 +45,7 @@ import type { ApiSendMessageAction, ApiSessionData, ApiStarGift, + ApiStarGiftAttributeOriginalDetails, ApiStarGiftUnique, ApiStarsSubscription, ApiStarsTransaction, @@ -2661,12 +2662,22 @@ export interface ActionPayloads { transferStars?: number; recipientId: string; } & WithTabId; + removeGiftDescription: { + gift: ApiInputSavedStarGift; + price: number; + } & WithTabId; closeGiftTransferModal: WithTabId | undefined; openGiftTransferConfirmModal: { gift: ApiSavedStarGift; recipientId: string; } & WithTabId; closeGiftTransferConfirmModal: WithTabId | undefined; + openGiftDescriptionRemoveModal: { + gift: ApiSavedStarGift; + price: number; + details: ApiStarGiftAttributeOriginalDetails; + } & WithTabId; + closeGiftDescriptionRemoveModal: WithTabId | undefined; updateSelectedGiftCollection: { peerId: string; collectionId: number; diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts index a8861b2ea..87337dbbf 100644 --- a/src/global/types/tabState.ts +++ b/src/global/types/tabState.ts @@ -42,6 +42,7 @@ import type { ApiStarGift, ApiStarGiftAttribute, ApiStarGiftAttributeCounter, + ApiStarGiftAttributeOriginalDetails, ApiStarGiftUnique, ApiStarGiveawayOption, ApiStarsSubscription, @@ -854,6 +855,12 @@ export type TabState = { recipientId: string; }; + giftDescriptionRemoveModal?: { + gift: ApiSavedStarGift; + price: number; + details: ApiStarGiftAttributeOriginalDetails; + }; + giftUpgradeModal?: { sampleAttributes: ApiStarGiftAttribute[]; recipientId?: string; diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 8cdfc45e2..16cea8ed6 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1292,6 +1292,9 @@ export interface LangPair { 'GiftTransferTitle': undefined; 'GiftTransferTON': undefined; 'GiftTransferConfirmButtonFree': undefined; + 'RemoveGiftDescriptionTitle': undefined; + 'RemoveGiftDescriptionConfirmText': undefined; + 'RemoveGiftDescriptionSuccessMessage': undefined; 'GiftUpgradeUniqueTitle': undefined; 'GiftUpgradeUniqueDescription': undefined; 'GiftUpgradeTransferableTitle': undefined; @@ -1717,6 +1720,8 @@ export interface LangPair { 'ConfirmBuyGiftForTonDescription': undefined; 'TitleGiftLocked': undefined; 'QuickPreview': undefined; + 'DropOriginalDetailsTransaction': undefined; + 'StarGiftReasonDropOriginalDetails': undefined; 'GiftAnUpgradeButton': undefined; 'GiftPrepaidUpgradeTransactionTitle': undefined; 'ActionStarGiftPrepaidUpgradedYou': undefined; @@ -2191,6 +2196,9 @@ export interface LangPairWithVariables { 'gift': V; 'peer': V; }; + 'RemoveGiftDescriptionButton': { + 'amount': V; + }; 'GiftPeerUpgradeText': { 'peer': V; };