Gift Auction: Refactoring (#6574)

This commit is contained in:
Alexander Zinchuk 2026-01-13 01:14:26 +01:00
parent c277ebd0c2
commit 54561ae7b3
18 changed files with 253 additions and 138 deletions

View File

@ -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<OwnProps>(
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<OwnProps>(
isAccountFrozen,
isAppConfigLoaded: global.isAppConfigLoaded,
isFoldersSidebarShown: foldersPosition === FOLDERS_POSITION_LEFT && !isMobile && selectAreFoldersPresent(global),
activeGiftAuction,
selectedGiftAuction,
};
},
)(Main));

View File

@ -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(() => {

View File

@ -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) && (
<div className={styles.bottomDescription}>
{lang('GiftAuctionDescription', {
count: giftsPerRound,
@ -394,7 +397,7 @@ function GiftComposer({
isLoading={isPaymentFormLoading}
noForcedUpperCase
>
{activeGiftAuction ? (
{giftAuction ? (
<div>
<div>
{lang('GiftAuctionPlaceBid')}
@ -459,7 +462,7 @@ function GiftComposer({
}
export default memo(withGlobal<OwnProps>(
(global, { peerId }): Complete<StateProps> => {
(global, { peerId, gift }): Complete<StateProps> => {
const theme = selectTheme(global);
const {
stars,
@ -481,6 +484,9 @@ export default memo(withGlobal<OwnProps>(
&& 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<OwnProps>(
paidMessagesStars,
areUniqueStarGiftsDisallowed,
shouldDisallowLimitedStarGifts,
activeGiftAuction: tabState.activeGiftAuction,
giftAuction,
};
},
)(GiftComposer));

View File

@ -105,7 +105,6 @@ const GiftModal: FC<OwnProps & StateProps> = ({
loadMyUniqueGifts,
openGiftTransferConfirmModal,
setGiftModalSelectedGift,
clearActiveGiftAuction,
} = getActions();
const dialogRef = useRef<HTMLDivElement>();
const transitionRef = useRef<HTMLDivElement>();
@ -440,7 +439,6 @@ const GiftModal: FC<OwnProps & StateProps> = ({
}
if (isGiftScreen) {
setGiftModalSelectedGift({ gift: undefined });
clearActiveGiftAuction();
return;
}
handleCloseModal();

View File

@ -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<OwnProps>(
(global): Complete<StateProps> => {
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,

View File

@ -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,

View File

@ -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 ? <GiftAuctionInfoModal {...props} /> : undefined;
};

View File

@ -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 (
<TableAboutModal
@ -92,10 +92,11 @@ const GiftAuctionInfoModal = ({
export default memo(withGlobal<OwnProps>(
(global): Complete<StateProps> => {
const { activeGiftAuction } = selectTabState(global);
const { giftAuctionInfoModal } = selectTabState(global);
const auctionGiftId = giftAuctionInfoModal?.auctionGiftId;
return {
activeGiftAuction,
giftAuction: auctionGiftId
? global.giftAuctionByGiftId?.[auctionGiftId] : undefined,
};
},
)(GiftAuctionInfoModal));

View File

@ -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<OwnProps>(
(global): Complete<StateProps> => {
const { activeGiftAuction } = selectTabState(global);
const { giftAuctionModal } = selectTabState(global);
const auctionGiftId = giftAuctionModal?.auctionGiftId;
return {
auctionState: activeGiftAuction,
auctionState: auctionGiftId
? global.giftAuctionByGiftId?.[auctionGiftId] : undefined,
};
},
)(GiftAuctionModal));

View File

@ -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,
};
}

View File

@ -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<void> => {
const { giftId, tabId = getCurrentTabId() } = payload;
addActionHandler('loadGiftAuction', async (global, _actions, payload): Promise<void> => {
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<void> => {
const { gift, peerId, tabId = getCurrentTabId() } = payload;

View File

@ -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;

View File

@ -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<void> => {
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);
});

View File

@ -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<T extends GlobalState>(
@ -57,68 +57,110 @@ function getAuctionStateVersion(state: ApiTypeStarGiftAuctionState): number {
return state.type === 'active' ? state.version : 0;
}
export function updateActiveGiftAuction<T extends GlobalState>(
export function updateGiftAuction<T extends GlobalState>(
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<T extends GlobalState>(
export function updateGiftAuctionState<T extends GlobalState>(
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<T extends GlobalState>(
export function updateGiftAuctionUserState<T extends GlobalState>(
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<T extends GlobalState>(
global: T,
auctionState: ApiStarGiftAuctionState,
): T {
const giftId = auctionState.gift.id;
return {
...global,
giftAuctionByGiftId: {
...global.giftAuctionByGiftId,
[giftId]: auctionState,
},
};
}
export function removeGiftAuction<T extends GlobalState>(
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,
};
}

View File

@ -202,3 +202,36 @@ export function selectPeerProfileColor<T extends GlobalState>(global: T, peer: A
if (key === undefined) return undefined;
return global.peerColors?.profile?.[key];
}
export function selectTabSelectedAuctionGiftId<T extends GlobalState>(
global: T, ...[tabId = getCurrentTabId()]: TabArgs<T>
) {
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<T extends GlobalState>(
global: T, ...[tabId = getCurrentTabId()]: TabArgs<T>
) {
const giftId = selectTabSelectedAuctionGiftId(global, tabId);
return giftId ? global.giftAuctionByGiftId?.[giftId] : undefined;
}
export function selectHasAnySelectedAuction<T extends GlobalState>(global: T, giftId: string) {
return Object.keys(global.byTabId).some((tabId) => {
return selectTabSelectedAuctionGiftId(global, Number(tabId)) === giftId;
});
}
export function selectShouldRemoveGiftAuction<T extends GlobalState>(global: T, giftId: string) {
return !selectHasAnySelectedAuction(global, giftId);
}

View File

@ -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;

View File

@ -34,6 +34,7 @@ import type {
ApiSavedStarGift,
ApiSession,
ApiSponsoredMessage,
ApiStarGiftAuctionState,
ApiStarGiftCollection,
ApiStarGiftRegular,
ApiStarsAmount,
@ -328,6 +329,7 @@ export type GlobalState = {
starGiftCollections?: {
byPeerId: Record<string, ApiStarGiftCollection[]>;
};
giftAuctionByGiftId?: Record<string, ApiStarGiftAuctionState>;
stickers: {
setsById: Record<string, ApiStickerSet>;

View File

@ -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;