diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 8948d4481..a93229b15 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -28,6 +28,7 @@ import { selectIsServiceChatReady, selectIsStoryViewerOpen, selectPerformanceSettingsValue, + selectTabSelectedGiftAuction, selectTabState, selectUser, } from '../../global/selectors'; @@ -145,7 +146,7 @@ type StateProps = { isAccountFrozen?: boolean; isAppConfigLoaded?: boolean; isFoldersSidebarShown: boolean; - activeGiftAuction?: ApiStarGiftAuctionState; + selectedGiftAuction?: ApiStarGiftAuctionState; }; const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min @@ -199,7 +200,7 @@ const Main = ({ isAccountFrozen, isAppConfigLoaded, isFoldersSidebarShown, - activeGiftAuction, + selectedGiftAuction, }: OwnProps & StateProps) => { const { initMain, @@ -260,7 +261,7 @@ const Main = ({ loadAllStories, loadAllHiddenStories, loadContentSettings, - loadActiveGiftAuction, + loadGiftAuction, loadPromoData, } = getActions(); @@ -442,12 +443,12 @@ const Main = ({ }); }, [currentUserId]); - // Refresh active gift auction subscription - const auctionTimeout = activeGiftAuction?.timeout; - const auctionGiftId = activeGiftAuction?.gift.id; + // Refresh gift auction subscription + const auctionTimeout = selectedGiftAuction?.state.type === 'active' ? selectedGiftAuction?.timeout : undefined; + const auctionGiftId = selectedGiftAuction?.gift.id; useInterval(() => { if (auctionGiftId) { - loadActiveGiftAuction({ giftId: auctionGiftId }); + loadGiftAuction({ giftId: auctionGiftId }); } }, auctionTimeout ? auctionTimeout * 1000 : undefined); @@ -647,9 +648,10 @@ export default memo(withGlobal( payment, limitReachedModal, deleteFolderDialogModal, - activeGiftAuction, } = selectTabState(global); + const selectedGiftAuction = selectTabSelectedGiftAuction(global); + const { wasTimeFormatSetManually, foldersPosition } = selectSharedSettings(global); const gameMessage = openedGame && selectChatMessage(global, openedGame.chatId, openedGame.messageId); @@ -706,7 +708,7 @@ export default memo(withGlobal( isAccountFrozen, isAppConfigLoaded: global.isAppConfigLoaded, isFoldersSidebarShown: foldersPosition === FOLDERS_POSITION_LEFT && !isMobile && selectAreFoldersPresent(global), - activeGiftAuction, + selectedGiftAuction, }; }, )(Main)); diff --git a/src/components/modals/gift/AboutStarGiftModal.tsx b/src/components/modals/gift/AboutStarGiftModal.tsx index d193741a3..04f289849 100644 --- a/src/components/modals/gift/AboutStarGiftModal.tsx +++ b/src/components/modals/gift/AboutStarGiftModal.tsx @@ -24,7 +24,7 @@ const AboutStarGiftModal = ({ const { closeAboutStarGiftModal } = getActions(); const lang = useLang(); - const isOpen = Boolean(modal?.isOpen); + const isOpen = Boolean(modal); const renderingModal = useCurrentOrPrev(modal); const handleClose = useLastCallback(() => { diff --git a/src/components/modals/gift/GiftComposer.tsx b/src/components/modals/gift/GiftComposer.tsx index 49516e19f..cfdc479da 100644 --- a/src/components/modals/gift/GiftComposer.tsx +++ b/src/components/modals/gift/GiftComposer.tsx @@ -57,7 +57,7 @@ export type StateProps = { paidMessagesStars?: number; areUniqueStarGiftsDisallowed?: boolean; shouldDisallowLimitedStarGifts?: boolean; - activeGiftAuction?: ApiStarGiftAuctionState; + giftAuction?: ApiStarGiftAuctionState; }; const LIMIT_DISPLAY_THRESHOLD = 50; @@ -80,7 +80,7 @@ function GiftComposer({ paidMessagesStars, areUniqueStarGiftsDisallowed, shouldDisallowLimitedStarGifts, - activeGiftAuction, + giftAuction, }: OwnProps & StateProps) { const { sendStarGift, sendPremiumGiftByStars, openInvoice, openGiftUpgradeModal, openStarsBalanceModal, @@ -189,14 +189,16 @@ function GiftComposer({ }); const handleLearnMoreClick = useLastCallback(() => { - openGiftAuctionInfoModal({}); + if (!giftAuction) return; + openGiftAuctionInfoModal({ auctionGiftId: giftAuction.gift.id }); }); const handleMainButtonClick = useLastCallback(() => { - if (activeGiftAuction) { - const existingBidPeerId = activeGiftAuction.userState.bidPeerId; + if (giftAuction) { + const existingBidPeerId = giftAuction.userState.bidPeerId; if (existingBidPeerId && existingBidPeerId !== peerId) { openGiftAuctionChangeRecipientModal({ + auctionGiftId: giftAuction.gift.id, oldPeerId: existingBidPeerId, newPeerId: peerId, message: giftMessage || undefined, @@ -206,6 +208,7 @@ function GiftComposer({ } openGiftAuctionBidModal({ + auctionGiftId: giftAuction.gift.id, peerId, message: giftMessage || undefined, shouldHideName: shouldHideName || undefined, @@ -360,8 +363,8 @@ function GiftComposer({ ? formatStarsAsIcon(lang, gift.stars + (shouldPayForUpgrade ? gift.upgradeStars! : 0), { asFont: true }) : isPremiumGift ? formatCurrency(lang, gift.amount, gift.currency) : undefined; - const giftsPerRound = activeGiftAuction?.gift.giftsPerRound; - const auctionEndDate = activeGiftAuction?.state.endDate; + const giftsPerRound = giftAuction?.gift.giftsPerRound; + const auctionEndDate = giftAuction?.state.endDate; const auctionTimeLeft = auctionEndDate ? auctionEndDate - getServerTime() : undefined; const shouldUseTextTimer = auctionTimeLeft !== undefined && auctionTimeLeft > 0 && auctionTimeLeft < TEXT_TIMER_THRESHOLD; @@ -379,7 +382,7 @@ function GiftComposer({ className={styles.limited} /> )} - {activeGiftAuction && Boolean(giftsPerRound) && ( + {giftAuction && Boolean(giftsPerRound) && (
{lang('GiftAuctionDescription', { count: giftsPerRound, @@ -394,7 +397,7 @@ function GiftComposer({ isLoading={isPaymentFormLoading} noForcedUpperCase > - {activeGiftAuction ? ( + {giftAuction ? (
{lang('GiftAuctionPlaceBid')} @@ -459,7 +462,7 @@ function GiftComposer({ } export default memo(withGlobal( - (global, { peerId }): Complete => { + (global, { peerId, gift }): Complete => { const theme = selectTheme(global); const { stars, @@ -481,6 +484,9 @@ export default memo(withGlobal( && userFullInfo?.disallowedGifts?.shouldDisallowLimitedStarGifts; const tabState = selectTabState(global); + const auctionGiftId = 'id' in gift && gift.type === 'starGift' && gift.isAuction ? gift.id : undefined; + const giftAuction = auctionGiftId + ? global.giftAuctionByGiftId?.[auctionGiftId] : undefined; return { starBalance: stars?.balance, @@ -496,7 +502,7 @@ export default memo(withGlobal( paidMessagesStars, areUniqueStarGiftsDisallowed, shouldDisallowLimitedStarGifts, - activeGiftAuction: tabState.activeGiftAuction, + giftAuction, }; }, )(GiftComposer)); diff --git a/src/components/modals/gift/GiftModal.tsx b/src/components/modals/gift/GiftModal.tsx index 4db26b53c..d3ccf6653 100644 --- a/src/components/modals/gift/GiftModal.tsx +++ b/src/components/modals/gift/GiftModal.tsx @@ -105,7 +105,6 @@ const GiftModal: FC = ({ loadMyUniqueGifts, openGiftTransferConfirmModal, setGiftModalSelectedGift, - clearActiveGiftAuction, } = getActions(); const dialogRef = useRef(); const transitionRef = useRef(); @@ -440,7 +439,6 @@ const GiftModal: FC = ({ } if (isGiftScreen) { setGiftModalSelectedGift({ gift: undefined }); - clearActiveGiftAuction(); return; } handleCloseModal(); diff --git a/src/components/modals/gift/auction/GiftAuctionBidModal.tsx b/src/components/modals/gift/auction/GiftAuctionBidModal.tsx index 9d8d998df..4978b6575 100644 --- a/src/components/modals/gift/auction/GiftAuctionBidModal.tsx +++ b/src/components/modals/gift/auction/GiftAuctionBidModal.tsx @@ -60,9 +60,9 @@ const GiftAuctionBidModal = ({ currentUserPeer, topBidderIds, }: OwnProps & StateProps) => { - const { closeGiftAuctionBidModal, sendStarGiftAuctionBid, loadActiveGiftAuction } = getActions(); + const { closeGiftAuctionBidModal, sendStarGiftAuctionBid, loadGiftAuction } = getActions(); - const isOpen = Boolean(modal?.isOpen); + const isOpen = Boolean(modal); const renderingAuctionState = useCurrentOrPrev(auctionState); const renderingTopBidderIds = useCurrentOrPrev(topBidderIds); @@ -125,8 +125,8 @@ const GiftAuctionBidModal = ({ }); const handleTimerEnd = useLastCallback(() => { - if (!renderingAuctionState?.gift.id) return; - loadActiveGiftAuction({ giftId: renderingAuctionState.gift.id }); + if (!modal?.auctionGiftId || !isOpen) return; + loadGiftAuction({ giftId: modal.auctionGiftId }); }); const handleRequestCustomValue = useLastCallback(() => { @@ -401,17 +401,20 @@ const GiftAuctionBidModal = ({ export default memo(withGlobal( (global): Complete => { - const { activeGiftAuction } = selectTabState(global); const { stars, currentUserId } = global; + const { giftAuctionBidModal } = selectTabState(global); + const auctionGiftId = giftAuctionBidModal?.auctionGiftId; + const giftAuction = auctionGiftId + ? global.giftAuctionByGiftId?.[auctionGiftId] : undefined; const currentUserPeer = currentUserId ? selectPeer(global, currentUserId) : undefined; - const topBidderIds = activeGiftAuction?.state.type === 'active' - ? activeGiftAuction.state.topBidders + const topBidderIds = giftAuction?.state.type === 'active' + ? giftAuction.state.topBidders : undefined; return { - auctionState: activeGiftAuction, + auctionState: giftAuction, starBalance: stars?.balance, currentUserPeer, topBidderIds, diff --git a/src/components/modals/gift/auction/GiftAuctionChangeRecipientModal.tsx b/src/components/modals/gift/auction/GiftAuctionChangeRecipientModal.tsx index b88572921..37a3c7cb1 100644 --- a/src/components/modals/gift/auction/GiftAuctionChangeRecipientModal.tsx +++ b/src/components/modals/gift/auction/GiftAuctionChangeRecipientModal.tsx @@ -33,16 +33,17 @@ const GiftAuctionChangeRecipientModal = ({ modal, oldPeer, newPeer }: OwnProps & const { closeGiftAuctionChangeRecipientModal, openGiftAuctionBidModal } = getActions(); const lang = useLang(); - const isOpen = Boolean(modal?.isOpen); + const isOpen = Boolean(modal); const renderingOldPeer = useCurrentOrPrev(oldPeer); const renderingNewPeer = useCurrentOrPrev(newPeer); const renderingModal = useCurrentOrPrev(modal); const handleConfirm = useLastCallback(() => { - if (!renderingModal) return; + if (!renderingModal?.auctionGiftId) return; closeGiftAuctionChangeRecipientModal(); openGiftAuctionBidModal({ + auctionGiftId: renderingModal.auctionGiftId, peerId: renderingModal.newPeerId, message: renderingModal.message, shouldHideName: renderingModal.shouldHideName, diff --git a/src/components/modals/gift/auction/GiftAuctionInfoModal.async.tsx b/src/components/modals/gift/auction/GiftAuctionInfoModal.async.tsx index 0ea0d7697..7eedc07ba 100644 --- a/src/components/modals/gift/auction/GiftAuctionInfoModal.async.tsx +++ b/src/components/modals/gift/auction/GiftAuctionInfoModal.async.tsx @@ -6,7 +6,7 @@ import useModuleLoader from '../../../../hooks/useModuleLoader'; const GiftAuctionInfoModalAsync = (props: OwnProps) => { const { modal } = props; - const GiftAuctionInfoModal = useModuleLoader(Bundles.Stars, 'GiftAuctionInfoModal', !modal?.isOpen); + const GiftAuctionInfoModal = useModuleLoader(Bundles.Stars, 'GiftAuctionInfoModal', !modal); return GiftAuctionInfoModal ? : undefined; }; diff --git a/src/components/modals/gift/auction/GiftAuctionInfoModal.tsx b/src/components/modals/gift/auction/GiftAuctionInfoModal.tsx index 12aaefca9..89c9f2a94 100644 --- a/src/components/modals/gift/auction/GiftAuctionInfoModal.tsx +++ b/src/components/modals/gift/auction/GiftAuctionInfoModal.tsx @@ -20,17 +20,17 @@ export type OwnProps = { }; type StateProps = { - activeGiftAuction?: ApiStarGiftAuctionState; + giftAuction?: ApiStarGiftAuctionState; }; const GiftAuctionInfoModal = ({ modal, - activeGiftAuction, + giftAuction, }: OwnProps & StateProps) => { const { closeGiftAuctionInfoModal } = getActions(); const lang = useLang(); - const isOpen = Boolean(modal?.isOpen && activeGiftAuction); + const isOpen = Boolean(modal && giftAuction); const handleClose = useLastCallback(() => { closeGiftAuctionInfoModal(); @@ -68,7 +68,7 @@ const GiftAuctionInfoModal = ({ }, [lang, isOpen, handleClose]); const listItemData = useMemo(() => { - const count = activeGiftAuction?.gift.giftsPerRound || 0; + const count = giftAuction?.gift.giftsPerRound || 0; return [ ['auction-drop', lang('GiftAuctionInfoTopBiddersTitle', { count }, { pluralValue: count }), lang('GiftAuctionInfoTopBiddersSubtitle', { count }, { pluralValue: count })], @@ -77,7 +77,7 @@ const GiftAuctionInfoModal = ({ ['stars-refund', lang('GiftAuctionInfoMissedBiddersTitle'), lang('GiftAuctionInfoMissedBiddersSubtitle')], ] satisfies TableAboutData; - }, [lang, activeGiftAuction]); + }, [lang, giftAuction]); return ( ( (global): Complete => { - const { activeGiftAuction } = selectTabState(global); - + const { giftAuctionInfoModal } = selectTabState(global); + const auctionGiftId = giftAuctionInfoModal?.auctionGiftId; return { - activeGiftAuction, + giftAuction: auctionGiftId + ? global.giftAuctionByGiftId?.[auctionGiftId] : undefined, }; }, )(GiftAuctionInfoModal)); diff --git a/src/components/modals/gift/auction/GiftAuctionModal.tsx b/src/components/modals/gift/auction/GiftAuctionModal.tsx index aef15ed49..5bbb4ce2d 100644 --- a/src/components/modals/gift/auction/GiftAuctionModal.tsx +++ b/src/components/modals/gift/auction/GiftAuctionModal.tsx @@ -57,7 +57,7 @@ const GiftAuctionModal = ({ modal, auctionState }: OwnProps & StateProps) => { openGiftInMarket, } = getActions(); - const isOpen = Boolean(modal?.isOpen); + const isOpen = Boolean(modal); const renderingModal = useCurrentOrPrev(modal); const renderingAuctionState = useCurrentOrPrev(auctionState); @@ -80,10 +80,12 @@ const GiftAuctionModal = ({ modal, auctionState }: OwnProps & StateProps) => { useInterval(updatePreviewAttributes, isOpen ? PREVIEW_UPDATE_INTERVAL : undefined, true); useEffect(() => { - if (isOpen && renderingModal?.sampleAttributes) { - updatePreviewAttributes(); - } else { - setPreviewAttributes(undefined); + if (isOpen) { + if (renderingModal?.sampleAttributes) { + updatePreviewAttributes(); + } else { + setPreviewAttributes(undefined); + } } }, [isOpen, renderingModal?.sampleAttributes]); @@ -96,7 +98,8 @@ const GiftAuctionModal = ({ modal, auctionState }: OwnProps & StateProps) => { const handleClose = useLastCallback(() => closeGiftAuctionModal()); const handleLearnMoreClick = useLastCallback(() => { - openGiftAuctionInfoModal({}); + if (!gift) return; + openGiftAuctionInfoModal({ auctionGiftId: gift.id }); }); const handleLearnMoreAboutGiftsClick = useLastCallback(() => { @@ -111,7 +114,7 @@ const GiftAuctionModal = ({ modal, auctionState }: OwnProps & StateProps) => { const handleJoinClick = useLastCallback(() => { if (!gift) return; - closeGiftAuctionModal({ shouldKeepActiveAuction: true }); + closeGiftAuctionModal({ shouldKeepAuction: true }); setGiftModalSelectedGift({ gift }); }); @@ -374,10 +377,11 @@ const GiftAuctionModal = ({ modal, auctionState }: OwnProps & StateProps) => { export default memo(withGlobal( (global): Complete => { - const { activeGiftAuction } = selectTabState(global); - + const { giftAuctionModal } = selectTabState(global); + const auctionGiftId = giftAuctionModal?.auctionGiftId; return { - auctionState: activeGiftAuction, + auctionState: auctionGiftId + ? global.giftAuctionByGiftId?.[auctionGiftId] : undefined, }; }, )(GiftAuctionModal)); diff --git a/src/components/modals/paidReaction/StarSlider.tsx b/src/components/modals/paidReaction/StarSlider.tsx index 5c40dcfc8..f63065671 100644 --- a/src/components/modals/paidReaction/StarSlider.tsx +++ b/src/components/modals/paidReaction/StarSlider.tsx @@ -42,6 +42,7 @@ const BEAK_HEIGHT = 32; const BEAK_OFFSET_END = 10; const TIP_OFFSET_END = 0; +const BEAK_TIP_PADDING = 1; const BADGE_HORIZONTAL_PADDING = 2 * REM; const BADGE_ICON_SIZE = 1.5 * REM; @@ -410,11 +411,14 @@ function calcBadgePosition( } } + const maxTipOffset = beakWidth / 2 - TIP_RADIUS - BEAK_TIP_PADDING; + const clampedTipOffset = Math.max(-maxTipOffset, Math.min(maxTipOffset, beakTipOffset)); + return { minBadgeX, maxBadgeX, beakOffset, - beakTipOffset, + beakTipOffset: clampedTipOffset, beakWidth, }; } diff --git a/src/global/actions/api/stars.ts b/src/global/actions/api/stars.ts index 4f3708910..9b36eeddf 100644 --- a/src/global/actions/api/stars.ts +++ b/src/global/actions/api/stars.ts @@ -16,8 +16,8 @@ import { addActionHandler, getGlobal, getPromiseActions, setGlobal } from '../.. import { appendStarsSubscriptions, appendStarsTransactions, + replaceGiftAuction, replacePeerSavedGifts, - updateActiveGiftAuction, updateChats, updatePeerStarGiftCollections, updateStarsBalance, @@ -584,24 +584,24 @@ addActionHandler('openGiftAuctionModal', async (global, _actions, payload): Prom const { gift, tabId = getCurrentTabId() } = payload; const [, preview] = await Promise.all([ - getPromiseActions().loadActiveGiftAuction({ giftId: gift.id, tabId }), + getPromiseActions().loadGiftAuction({ giftId: gift.id }), callApi('fetchStarGiftUpgradePreview', { giftId: gift.id }), ]); global = getGlobal(); global = updateTabState(global, { giftAuctionModal: { - isOpen: true, + auctionGiftId: gift.id, sampleAttributes: preview?.sampleAttributes, }, }, tabId); setGlobal(global); }); -addActionHandler('loadActiveGiftAuction', async (global, _actions, payload): Promise => { - const { giftId, tabId = getCurrentTabId() } = payload; +addActionHandler('loadGiftAuction', async (global, _actions, payload): Promise => { + const { giftId } = payload; - const currentAuction = selectTabState(global, tabId).activeGiftAuction; + const currentAuction = global.giftAuctionByGiftId?.[giftId]; const currentVersion = currentAuction?.state.type === 'active' ? currentAuction.state.version : 0; const auctionState = await callApi('fetchStarGiftAuctionState', { @@ -611,18 +611,10 @@ addActionHandler('loadActiveGiftAuction', async (global, _actions, payload): Pro if (!auctionState) return; global = getGlobal(); - global = updateActiveGiftAuction(global, auctionState, tabId); + global = replaceGiftAuction(global, auctionState); setGlobal(global); }); -addActionHandler('clearActiveGiftAuction', (global, _actions, payload): ActionReturnType => { - const { tabId = getCurrentTabId() } = payload || {}; - - return updateTabState(global, { - activeGiftAuction: undefined, - }, tabId); -}); - addActionHandler('toggleSavedGiftPinned', async (global, actions, payload): Promise => { const { gift, peerId, tabId = getCurrentTabId() } = payload; diff --git a/src/global/actions/apiUpdaters/payments.ts b/src/global/actions/apiUpdaters/payments.ts index 3fd068987..ad9f5423e 100644 --- a/src/global/actions/apiUpdaters/payments.ts +++ b/src/global/actions/apiUpdaters/payments.ts @@ -6,8 +6,8 @@ import { getPeerTitle } from '../../helpers/peers'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { removeGiftInfoOriginalDetails, - updateActiveGiftAuctionState, - updateActiveGiftAuctionUserState, + updateGiftAuctionState, + updateGiftAuctionUserState, updateStarsBalance, } from '../../reducers'; import { updateTabState } from '../../reducers/tabs'; @@ -216,8 +216,8 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { } if (inputInvoice?.type === 'stargiftAuctionBid') { - const { activeGiftAuction } = selectTabState(global, tabId); - const giftsPerRound = activeGiftAuction?.gift.giftsPerRound; + const giftAuction = global.giftAuctionByGiftId?.[inputInvoice.giftId]; + const giftsPerRound = giftAuction?.gift.giftsPerRound; actions.showNotification({ icon: 'auction-filled', @@ -231,8 +231,8 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { tabId, }); - if (activeGiftAuction?.gift.id === inputInvoice.giftId) { - actions.loadActiveGiftAuction({ giftId: inputInvoice.giftId, tabId }); + if (giftAuction) { + actions.loadGiftAuction({ giftId: inputInvoice.giftId }); } } @@ -251,10 +251,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { case 'updateStarGiftAuctionState': { const { giftId, state } = update; - Object.keys(global.byTabId).forEach((tabIdStr) => { - const tabId = Number(tabIdStr); - global = updateActiveGiftAuctionState(global, giftId, state, tabId); - }); + global = updateGiftAuctionState(global, giftId, state); setGlobal(global); break; @@ -263,10 +260,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { case 'updateStarGiftAuctionUserState': { const { giftId, userState } = update; - Object.keys(global.byTabId).forEach((tabIdStr) => { - const tabId = Number(tabIdStr); - global = updateActiveGiftAuctionUserState(global, giftId, userState, tabId); - }); + global = updateGiftAuctionUserState(global, giftId, userState); setGlobal(global); break; diff --git a/src/global/actions/ui/stars.ts b/src/global/actions/ui/stars.ts index eaf73820d..3debefb47 100644 --- a/src/global/actions/ui/stars.ts +++ b/src/global/actions/ui/stars.ts @@ -11,11 +11,11 @@ import { callApi } from '../../../api/gramjs'; import { addTabStateResetterAction } from '../../helpers/meta'; import { getPrizeStarsTransactionFromGiveaway, getStarsTransactionFromGift } from '../../helpers/payments'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; -import { clearStarPayment, openStarsTransactionModal, -} from '../../reducers'; +import { clearStarPayment, openStarsTransactionModal } from '../../reducers'; +import { removeGiftAuction } from '../../reducers/gifts'; import { updateTabState } from '../../reducers/tabs'; import { - selectChatMessage, selectIsCurrentUserFrozen, selectStarsPayment, selectTabState, + selectChatMessage, selectIsCurrentUserFrozen, selectShouldRemoveGiftAuction, selectStarsPayment, selectTabState, } from '../../selectors'; addActionHandler('processOriginStarsPayment', (global, actions, payload): ActionReturnType => { @@ -210,6 +210,17 @@ addActionHandler('setGiftModalSelectedGift', (global, actions, payload): ActionR const tabState = selectTabState(global, tabId); const giftModal = tabState?.giftModal; + + if (!gift) { + const previousGift = giftModal?.selectedGift; + const auctionGiftId = previousGift && 'id' in previousGift && previousGift.type === 'starGift' + && previousGift.isAuction ? previousGift.id : undefined; + + if (auctionGiftId && selectShouldRemoveGiftAuction(global, auctionGiftId)) { + global = removeGiftAuction(global, auctionGiftId); + } + } + if (giftModal) { return updateTabState(global, { giftModal: { @@ -412,34 +423,56 @@ addTabStateResetterAction('closeGiftResalePriceComposerModal', 'giftResalePriceC addTabStateResetterAction('closeGiftUpgradeModal', 'giftUpgradeModal'); addActionHandler('closeGiftAuctionModal', (global, _actions, payload): ActionReturnType => { - const { shouldKeepActiveAuction, tabId = getCurrentTabId() } = payload || {}; + const { shouldKeepAuction, tabId = getCurrentTabId() } = payload || {}; const tabState = selectTabState(global, tabId); + const giftId = tabState.giftAuctionModal?.auctionGiftId; - return updateTabState(global, { + global = updateTabState(global, { giftAuctionModal: undefined, - activeGiftAuction: shouldKeepActiveAuction ? tabState?.activeGiftAuction : undefined, }, tabId); + + if (!shouldKeepAuction && giftId && selectShouldRemoveGiftAuction(global, giftId)) { + global = removeGiftAuction(global, giftId); + } + + return global; }); addActionHandler('openGiftAuctionBidModal', (global, _actions, payload): ActionReturnType => { - const { peerId, message, shouldHideName, tabId = getCurrentTabId() } = payload || {}; + const { auctionGiftId, peerId, message, shouldHideName, tabId = getCurrentTabId() } = payload; return updateTabState(global, { - giftAuctionBidModal: { isOpen: true, peerId, message, shouldHideName }, + giftAuctionBidModal: { auctionGiftId, peerId, message, shouldHideName }, }, tabId); }); -addTabStateResetterAction('closeGiftAuctionBidModal', 'giftAuctionBidModal'); - -addActionHandler('openGiftAuctionInfoModal', (global, _actions, payload): ActionReturnType => { +addActionHandler('closeGiftAuctionBidModal', (global, _actions, payload): ActionReturnType => { const { tabId = getCurrentTabId() } = payload || {}; + global = updateTabState(global, { + giftAuctionBidModal: undefined, + }, tabId); + + return global; +}); + +addActionHandler('openGiftAuctionInfoModal', (global, _actions, payload): ActionReturnType => { + const { auctionGiftId, tabId = getCurrentTabId() } = payload; + return updateTabState(global, { - giftAuctionInfoModal: { isOpen: true }, + giftAuctionInfoModal: { auctionGiftId }, }, tabId); }); -addTabStateResetterAction('closeGiftAuctionInfoModal', 'giftAuctionInfoModal'); +addActionHandler('closeGiftAuctionInfoModal', (global, _actions, payload): ActionReturnType => { + const { tabId = getCurrentTabId() } = payload || {}; + + global = updateTabState(global, { + giftAuctionInfoModal: undefined, + }, tabId); + + return global; +}); addActionHandler('openAboutStarGiftModal', async (global, _actions, payload): Promise => { const { tabId = getCurrentTabId() } = payload || {}; @@ -460,7 +493,7 @@ addActionHandler('openAboutStarGiftModal', async (global, _actions, payload): Pr global = getGlobal(); global = updateTabState(global, { - aboutStarGiftModal: { isOpen: true, videoId, videoThumbnail }, + aboutStarGiftModal: { videoId, videoThumbnail }, }, tabId); setGlobal(global); }); @@ -469,12 +502,12 @@ addTabStateResetterAction('closeAboutStarGiftModal', 'aboutStarGiftModal'); addActionHandler('openGiftAuctionChangeRecipientModal', (global, _actions, payload): ActionReturnType => { const { - oldPeerId, newPeerId, message, shouldHideName, tabId = getCurrentTabId(), + auctionGiftId, oldPeerId, newPeerId, message, shouldHideName, tabId = getCurrentTabId(), } = payload; return updateTabState(global, { giftAuctionChangeRecipientModal: { - isOpen: true, oldPeerId, newPeerId, message, shouldHideName, + auctionGiftId, oldPeerId, newPeerId, message, shouldHideName, }, }, tabId); }); diff --git a/src/global/reducers/gifts.ts b/src/global/reducers/gifts.ts index 59251e80e..edc15ac8d 100644 --- a/src/global/reducers/gifts.ts +++ b/src/global/reducers/gifts.ts @@ -7,7 +7,7 @@ import type { import type { GlobalState } from '../types'; import { getCurrentTabId } from '../../util/establishMultitabRole'; -import { selectTabState } from '../selectors'; +import { omit } from '../../util/iteratees'; import { updateTabState } from './tabs'; export function removeGiftInfoOriginalDetails( @@ -57,68 +57,110 @@ function getAuctionStateVersion(state: ApiTypeStarGiftAuctionState): number { return state.type === 'active' ? state.version : 0; } -export function updateActiveGiftAuction( +export function updateGiftAuction( global: T, auctionState: ApiStarGiftAuctionState, - tabId: number = getCurrentTabId(), ): T { - const currentAuction = selectTabState(global, tabId).activeGiftAuction; + const giftId = auctionState.gift.id; + const currentAuction = global.giftAuctionByGiftId?.[giftId]; + + if (!currentAuction) return global; const serverVersion = getAuctionStateVersion(auctionState.state); - const clientVersion = currentAuction ? getAuctionStateVersion(currentAuction.state) : -1; + const clientVersion = getAuctionStateVersion(currentAuction.state); if (serverVersion >= clientVersion) { - return updateTabState(global, { - activeGiftAuction: auctionState, - }, tabId); + return { + ...global, + giftAuctionByGiftId: { + ...global.giftAuctionByGiftId, + [giftId]: auctionState, + }, + }; } return global; } -export function updateActiveGiftAuctionState( +export function updateGiftAuctionState( global: T, giftId: string, state: ApiTypeStarGiftAuctionState, - tabId: number, ): T { - const activeAuction = selectTabState(global, tabId).activeGiftAuction; + const giftAuction = global.giftAuctionByGiftId?.[giftId]; - if (!activeAuction || activeAuction.gift.id !== giftId) { + if (!giftAuction) { return global; } const serverVersion = getAuctionStateVersion(state); - const clientVersion = getAuctionStateVersion(activeAuction.state); + const clientVersion = getAuctionStateVersion(giftAuction.state); if (serverVersion > clientVersion) { - return updateTabState(global, { - activeGiftAuction: { - ...activeAuction, - state, + return { + ...global, + giftAuctionByGiftId: { + ...global.giftAuctionByGiftId, + [giftId]: { + ...giftAuction, + state, + }, }, - }, tabId); + }; } return global; } -export function updateActiveGiftAuctionUserState( +export function updateGiftAuctionUserState( global: T, giftId: string, userState: ApiStarGiftAuctionUserState, - tabId: number, ): T { - const activeAuction = selectTabState(global, tabId).activeGiftAuction; + const giftAuction = global.giftAuctionByGiftId?.[giftId]; - if (!activeAuction || activeAuction.gift.id !== giftId) { + if (!giftAuction) { return global; } - return updateTabState(global, { - activeGiftAuction: { - ...activeAuction, - userState, + return { + ...global, + giftAuctionByGiftId: { + ...global.giftAuctionByGiftId, + [giftId]: { + ...giftAuction, + userState, + }, }, - }, tabId); + }; +} + +export function replaceGiftAuction( + global: T, + auctionState: ApiStarGiftAuctionState, +): T { + const giftId = auctionState.gift.id; + return { + ...global, + giftAuctionByGiftId: { + ...global.giftAuctionByGiftId, + [giftId]: auctionState, + }, + }; +} + +export function removeGiftAuction( + global: T, + giftId: string, +): T { + if (!global.giftAuctionByGiftId?.[giftId]) { + return global; + } + + const rest = omit(global.giftAuctionByGiftId, [giftId]); + + return { + ...global, + giftAuctionByGiftId: Object.keys(rest).length ? rest : undefined, + }; } diff --git a/src/global/selectors/ui.ts b/src/global/selectors/ui.ts index dc42c1efa..71c3f9840 100644 --- a/src/global/selectors/ui.ts +++ b/src/global/selectors/ui.ts @@ -202,3 +202,36 @@ export function selectPeerProfileColor(global: T, peer: A if (key === undefined) return undefined; return global.peerColors?.profile?.[key]; } + +export function selectTabSelectedAuctionGiftId( + global: T, ...[tabId = getCurrentTabId()]: TabArgs +) { + const tabState = selectTabState(global, tabId); + + const selectedGift = tabState.giftModal?.selectedGift; + + const giftModalAuctionGiftId = selectedGift && 'id' in selectedGift && + selectedGift.type === 'starGift' && selectedGift.isAuction ? selectedGift.id : undefined; + + return tabState.giftAuctionModal?.auctionGiftId + || tabState.giftAuctionBidModal?.auctionGiftId + || tabState.giftAuctionInfoModal?.auctionGiftId + || giftModalAuctionGiftId; +} + +export function selectTabSelectedGiftAuction( + global: T, ...[tabId = getCurrentTabId()]: TabArgs +) { + const giftId = selectTabSelectedAuctionGiftId(global, tabId); + return giftId ? global.giftAuctionByGiftId?.[giftId] : undefined; +} + +export function selectHasAnySelectedAuction(global: T, giftId: string) { + return Object.keys(global.byTabId).some((tabId) => { + return selectTabSelectedAuctionGiftId(global, Number(tabId)) === giftId; + }); +} + +export function selectShouldRemoveGiftAuction(global: T, giftId: string) { + return !selectHasAnySelectedAuction(global, giftId); +} diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index 300e492b0..233c6283e 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -2699,19 +2699,23 @@ export interface ActionPayloads { gift: ApiStarGiftRegular; } & WithTabId; closeGiftAuctionModal: { - shouldKeepActiveAuction?: boolean; + shouldKeepAuction?: boolean; } & WithTabId | undefined; openGiftAuctionBidModal: { + auctionGiftId: string; peerId?: string; message?: string; shouldHideName?: boolean; - } & WithTabId | undefined; + } & WithTabId; closeGiftAuctionBidModal: WithTabId | undefined; - openGiftAuctionInfoModal: WithTabId | undefined; + openGiftAuctionInfoModal: { + auctionGiftId: string; + } & WithTabId; closeGiftAuctionInfoModal: WithTabId | undefined; openAboutStarGiftModal: WithTabId | undefined; closeAboutStarGiftModal: WithTabId | undefined; openGiftAuctionChangeRecipientModal: { + auctionGiftId: string; oldPeerId: string; newPeerId: string; message?: string; @@ -2732,10 +2736,9 @@ export interface ActionPayloads { shouldHideName?: boolean; isUpdateBid?: boolean; } & WithTabId; - loadActiveGiftAuction: { + loadGiftAuction: { giftId: string; - } & WithTabId; - clearActiveGiftAuction: WithTabId | undefined; + }; processStarGiftWithdrawal: { gift: ApiInputSavedStarGift; password: string; diff --git a/src/global/types/globalState.ts b/src/global/types/globalState.ts index e0882c595..fc915494b 100644 --- a/src/global/types/globalState.ts +++ b/src/global/types/globalState.ts @@ -34,6 +34,7 @@ import type { ApiSavedStarGift, ApiSession, ApiSponsoredMessage, + ApiStarGiftAuctionState, ApiStarGiftCollection, ApiStarGiftRegular, ApiStarsAmount, @@ -328,6 +329,7 @@ export type GlobalState = { starGiftCollections?: { byPeerId: Record; }; + giftAuctionByGiftId?: Record; stickers: { setsById: Record; diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts index 862573d25..818b33e7a 100644 --- a/src/global/types/tabState.ts +++ b/src/global/types/tabState.ts @@ -45,7 +45,6 @@ import type { ApiStarGiftAttributeCounter, ApiStarGiftAttributeOriginalDetails, ApiStarGiftAuctionAcquiredGift, - ApiStarGiftAuctionState, ApiStarGiftUnique, ApiStarGiftUpgradePrice, ApiStarGiveawayOption, @@ -708,7 +707,6 @@ export type TabState = { selectedResaleGift?: ApiStarGift; selectedGift?: ApiPremiumGiftCodeOption | ApiStarGift; }; - activeGiftAuction?: ApiStarGiftAuctionState; chatRefundModal?: { userId: string; starsToRefund: number; @@ -895,29 +893,28 @@ export type TabState = { }; giftAuctionModal?: { - isOpen?: boolean; + auctionGiftId: string; sampleAttributes?: ApiStarGiftAttribute[]; }; giftAuctionBidModal?: { - isOpen?: boolean; + auctionGiftId: string; peerId?: string; message?: string; shouldHideName?: boolean; }; giftAuctionInfoModal?: { - isOpen?: boolean; + auctionGiftId: string; }; aboutStarGiftModal?: { - isOpen?: boolean; videoId?: string; videoThumbnail?: ApiThumbnail; }; giftAuctionChangeRecipientModal?: { - isOpen?: boolean; + auctionGiftId: string; oldPeerId?: string; newPeerId?: string; message?: string;