Chat List: Show active auctions (#6641)
This commit is contained in:
parent
31eaf0e5dd
commit
d0ba0c2530
@ -442,7 +442,7 @@ export function buildApiStarGiftAuctionUserState(
|
||||
}
|
||||
|
||||
export function buildApiStarGiftAuctionState(
|
||||
result: GramJs.payments.StarGiftAuctionState,
|
||||
result: GramJs.payments.StarGiftAuctionState | GramJs.StarGiftActiveAuctionState,
|
||||
): ApiStarGiftAuctionState | undefined {
|
||||
const gift = buildApiStarGift(result.gift);
|
||||
if (gift.type !== 'starGift') return undefined;
|
||||
@ -454,7 +454,7 @@ export function buildApiStarGiftAuctionState(
|
||||
gift,
|
||||
state,
|
||||
userState: buildApiStarGiftAuctionUserState(result.userState),
|
||||
timeout: result.timeout,
|
||||
timeout: 'timeout' in result ? result.timeout : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -459,6 +459,20 @@ export async function fetchStarGiftAuctionAcquiredGifts({
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchStarGiftActiveAuctions() {
|
||||
const result = await invokeRequest(new GramJs.payments.GetStarGiftActiveAuctions({
|
||||
hash: DEFAULT_PRIMITIVES.BIGINT,
|
||||
}));
|
||||
|
||||
if (!result || result instanceof GramJs.payments.StarGiftActiveAuctionsNotModified) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
auctions: result.auctions.map(buildApiStarGiftAuctionState).filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
export function upgradeStarGift({
|
||||
inputSavedGift,
|
||||
shouldKeepOriginalDetails,
|
||||
|
||||
@ -387,7 +387,7 @@ export interface ApiStarGiftAuctionState {
|
||||
gift: ApiStarGiftRegular;
|
||||
state: ApiTypeStarGiftAuctionState;
|
||||
userState: ApiStarGiftAuctionUserState;
|
||||
timeout: number;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export interface ApiStarGiftAuctionAcquiredGift {
|
||||
|
||||
@ -2508,6 +2508,11 @@
|
||||
"GiftAuctionWonNotification" = "You won {gift} at the auction!";
|
||||
"GiftAuctionLearnMoreAboutGifts" = "Learn more about Telegram Gifts >";
|
||||
"GiftAuctionLearnMoreMenuItem" = "Learn More";
|
||||
"GiftAuctionBidPosition" = "Your bid of {amount} is ranked #{position}";
|
||||
"GiftAuctionListRaiseBid" = "Raise";
|
||||
"GiftAuctionListRound" = "Round {current} of {total}";
|
||||
"GiftAuctionActiveTitle" = "Active Auctions";
|
||||
"GiftAuctionNoActive" = "You are not participating in any auctions";
|
||||
"StarGiftInfoTitle" = "Telegram Gifts";
|
||||
"StarGiftInfoSubtitle" = "Gifts are collectible items you can trade or showcase on your profile.";
|
||||
"StarGiftInfoUniqueTitle" = "Unique";
|
||||
@ -2603,3 +2608,9 @@
|
||||
"SettingsDataClearMediaCache" = "Clear Media Cache";
|
||||
"SettingsDataClearMediaCacheDescription" = "Deletes locally cached media for this account";
|
||||
"SettingsDataClearMediaDone" = "Media cache cleared";
|
||||
"ChatListAuctionTitle_one" = "Active Auction";
|
||||
"ChatListAuctionTitle_other" = "{count} Active Auctions";
|
||||
"ChatListAuctionWinning" = "You are winning";
|
||||
"ChatListAuctionOutbid" = "You've been outbid";
|
||||
"ChatListAuctionMixed" = "You are winning in {winCount} and outbid in {outbidCount}";
|
||||
"ChatListAuctionView" = "View";
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
/* eslint-disable @stylistic/max-len */
|
||||
|
||||
export { default as StarsGiftModal } from '../components/modals/stars/gift/StarsGiftModal';
|
||||
export { default as StarsGiftingPickerModal } from '../components/main/premium/StarsGiftingPickerModal';
|
||||
export { default as StarsBalanceModal } from '../components/modals/stars/StarsBalanceModal';
|
||||
@ -29,3 +28,4 @@ export { default as GiftDescriptionRemoveModal } from '../components/modals/gift
|
||||
export { default as GiftOfferAcceptModal } from '../components/modals/gift/offer/GiftOfferAcceptModal';
|
||||
export { default as ChatRefundModal } from '../components/modals/stars/chatRefund/ChatRefundModal';
|
||||
export { default as PriceConfirmModal } from '../components/modals/priceConfirm/PriceConfirmModal';
|
||||
export { default as ActiveGiftAuctionsModal } from '../components/modals/gift/auction/ActiveGiftAuctionsModal';
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { ElementRef, FC } from '../../lib/teact/teact';
|
||||
import type { ElementRef } from '../../lib/teact/teact';
|
||||
import { memo, useRef, useState } from '../../lib/teact/teact';
|
||||
import { getGlobal } from '../../global';
|
||||
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
import { ApiMessageEntityTypes } from '../../api/types';
|
||||
import { ApiMessageEntityTypes, type ApiSticker } from '../../api/types';
|
||||
|
||||
import { selectIsAlwaysHighPriorityEmoji } from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -21,7 +21,6 @@ import blankImg from '../../assets/blank.png';
|
||||
|
||||
type OwnProps = {
|
||||
ref?: ElementRef<HTMLDivElement>;
|
||||
documentId: string;
|
||||
className?: string;
|
||||
style?: string;
|
||||
size?: number;
|
||||
@ -42,13 +41,20 @@ type OwnProps = {
|
||||
observeIntersectionForPlaying?: ObserveFn;
|
||||
onClick?: NoneToVoidFunction;
|
||||
onAnimationEnd?: NoneToVoidFunction;
|
||||
};
|
||||
} & ({
|
||||
documentId: string;
|
||||
sticker?: undefined;
|
||||
} | {
|
||||
sticker: ApiSticker;
|
||||
documentId?: undefined;
|
||||
});
|
||||
|
||||
const STICKER_SIZE = 20;
|
||||
|
||||
const CustomEmoji: FC<OwnProps> = ({
|
||||
const CustomEmoji = ({
|
||||
ref,
|
||||
documentId,
|
||||
sticker,
|
||||
className,
|
||||
style,
|
||||
size = STICKER_SIZE,
|
||||
@ -69,14 +75,15 @@ const CustomEmoji: FC<OwnProps> = ({
|
||||
observeIntersectionForPlaying,
|
||||
onClick,
|
||||
onAnimationEnd,
|
||||
}) => {
|
||||
}: OwnProps) => {
|
||||
let containerRef = useRef<HTMLDivElement>();
|
||||
if (ref) {
|
||||
containerRef = ref;
|
||||
}
|
||||
|
||||
// An alternative to `withGlobal` to avoid adding numerous global containers
|
||||
const { customEmoji, canPlay } = useCustomEmoji(documentId);
|
||||
const { customEmoji: customEmojiFromDocumentId, canPlay } = useCustomEmoji(documentId);
|
||||
const customEmoji = customEmojiFromDocumentId || sticker;
|
||||
|
||||
const loopCountRef = useRef(0);
|
||||
const [shouldPlay, setShouldPlay] = useState(true);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type {
|
||||
ApiAuctionBidLevel,
|
||||
ApiStarGift,
|
||||
ApiStarGiftAttribute,
|
||||
ApiStarGiftAttributeBackdrop,
|
||||
@ -122,6 +123,19 @@ export function preloadGiftAttributeStickers(attributes: ApiStarGiftAttribute[])
|
||||
});
|
||||
}
|
||||
|
||||
export function getBidAuctionPosition(bidAmount: number, bidDate: number, bidLevels: ApiAuctionBidLevel[]) {
|
||||
if (!bidLevels.length) return 1;
|
||||
|
||||
for (const level of bidLevels) {
|
||||
if (level.amount < bidAmount
|
||||
|| (level.amount === bidAmount && level.date >= bidDate)) {
|
||||
return level.pos;
|
||||
}
|
||||
}
|
||||
|
||||
return bidLevels[bidLevels.length - 1].pos + 1;
|
||||
}
|
||||
|
||||
export function getGiftRarityTitle(
|
||||
lang: LangFn,
|
||||
rarity: ApiStarGiftAttributeModel['rarity'],
|
||||
|
||||
@ -17,6 +17,7 @@ import { useSignalEffect } from '../../../../hooks/useSignalEffect';
|
||||
import { applyAnimationState, type PaneState } from '../../../middle/hooks/useHeaderPane';
|
||||
|
||||
import FrozenAccountPane from './FrozenAccountPane';
|
||||
import GiftAuctionPane from './GiftAuctionPane';
|
||||
import SuggestionPane from './SuggestionPane';
|
||||
import UnconfirmedSessionPane from './UnconfirmedSessionPane';
|
||||
|
||||
@ -34,6 +35,7 @@ type StateProps = {
|
||||
};
|
||||
|
||||
const TOP_MARGIN = 0.5 * REM;
|
||||
const ITEM_MARGIN = 0.25 * REM;
|
||||
const BOTTOM_MARGIN = 0.25 * REM;
|
||||
const FALLBACK_PANE_STATE = { height: 0 };
|
||||
|
||||
@ -46,6 +48,7 @@ const ChatListPanes = ({
|
||||
}: OwnProps & StateProps) => {
|
||||
const [getUnconfirmedSessionHeight, setUnconfirmedSessionHeight] = useSignal<PaneState>(FALLBACK_PANE_STATE);
|
||||
const [getFrozenAccountState, setFrozenAccountState] = useSignal<PaneState>(FALLBACK_PANE_STATE);
|
||||
const [getGiftAuctionState, setGiftAuctionState] = useSignal<PaneState>(FALLBACK_PANE_STATE);
|
||||
const [getSuggestionState, setSuggestionState] = useSignal<PaneState>(FALLBACK_PANE_STATE);
|
||||
|
||||
const isFirstRenderRef = useRef(true);
|
||||
@ -72,20 +75,30 @@ const ChatListPanes = ({
|
||||
|
||||
const canShowUnconfirmedSession = !isAccountFrozen && unconfirmedSession;
|
||||
const canShowSuggestions = !isAccountFrozen && !unconfirmedSession && promoData;
|
||||
const canShowGiftAuctions = !isAccountFrozen;
|
||||
|
||||
useSignalEffect(() => {
|
||||
const unconfirmedSessionHeight = getUnconfirmedSessionHeight();
|
||||
const frozenAccountHeight = getFrozenAccountState();
|
||||
const giftAuctionHeight = getGiftAuctionState();
|
||||
const suggestionHeight = getSuggestionState();
|
||||
|
||||
// Keep in sync with the order of the panes in the DOM
|
||||
const stateArray = [unconfirmedSessionHeight, frozenAccountHeight, suggestionHeight];
|
||||
const stateArray = [
|
||||
{ height: TOP_MARGIN, isSpacer: true },
|
||||
unconfirmedSessionHeight,
|
||||
frozenAccountHeight,
|
||||
giftAuctionHeight,
|
||||
{ height: giftAuctionHeight.height ? ITEM_MARGIN : 0, isSpacer: true },
|
||||
suggestionHeight,
|
||||
{ height: BOTTOM_MARGIN, isSpacer: true },
|
||||
];
|
||||
|
||||
const isFirstRender = isFirstRenderRef.current;
|
||||
const panelsHeight = stateArray.reduce((acc, state) => acc + state.height, 0);
|
||||
const totalHeight = panelsHeight ? panelsHeight + BOTTOM_MARGIN : 0;
|
||||
const totalHeight = stateArray.reduce((acc, state) => acc + state.height, 0);
|
||||
const panelsHeight = totalHeight - TOP_MARGIN - BOTTOM_MARGIN;
|
||||
|
||||
onHeightChange(totalHeight);
|
||||
onHeightChange(panelsHeight !== 0 ? totalHeight : 0);
|
||||
|
||||
const leftColumn = document.getElementById('LeftColumn');
|
||||
if (!leftColumn) return;
|
||||
@ -93,7 +106,6 @@ const ChatListPanes = ({
|
||||
applyAnimationState({
|
||||
list: stateArray,
|
||||
noTransition: isFirstRender,
|
||||
topMargin: TOP_MARGIN,
|
||||
zIndexIncrease: true,
|
||||
});
|
||||
|
||||
@ -102,7 +114,7 @@ const ChatListPanes = ({
|
||||
'--chat-list-panes-height': `${totalHeight}px`,
|
||||
});
|
||||
});
|
||||
}, [getUnconfirmedSessionHeight, getFrozenAccountState, getSuggestionState]);
|
||||
}, [getUnconfirmedSessionHeight, getFrozenAccountState, getSuggestionState, getGiftAuctionState]);
|
||||
|
||||
if (!shouldRender) return undefined;
|
||||
|
||||
@ -124,6 +136,10 @@ const ChatListPanes = ({
|
||||
unconfirmedSession={canShowUnconfirmedSession ? unconfirmedSession : undefined}
|
||||
onPaneStateChange={setUnconfirmedSessionHeight}
|
||||
/>
|
||||
<GiftAuctionPane
|
||||
canShow={canShowGiftAuctions}
|
||||
onPaneStateChange={setGiftAuctionState}
|
||||
/>
|
||||
<SuggestionPane
|
||||
promoData={canShowSuggestions ? promoData : undefined}
|
||||
onPaneStateChange={setSuggestionState}
|
||||
|
||||
30
src/components/left/main/panes/ChatPane.module.scss
Normal file
30
src/components/left/main/panes/ChatPane.module.scss
Normal file
@ -0,0 +1,30 @@
|
||||
@use "../../../../styles/mixins";
|
||||
|
||||
.pane {
|
||||
@include mixins.chat-list-pane;
|
||||
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
border-radius: var(--border-radius-default);
|
||||
line-height: 1.25;
|
||||
background-color: var(--color-background-secondary);
|
||||
|
||||
&:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
--emoji-size: 1.125rem;
|
||||
--custom-emoji-size: var(--emoji-size);
|
||||
|
||||
font-size: 0.9375rem;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
--emoji-size: 1rem;
|
||||
--custom-emoji-size: var(--emoji-size);
|
||||
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
36
src/components/left/main/panes/GiftAuctionPane.module.scss
Normal file
36
src/components/left/main/panes/GiftAuctionPane.module.scss
Normal file
@ -0,0 +1,36 @@
|
||||
.root {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 2;
|
||||
}
|
||||
|
||||
.giftEmojis {
|
||||
margin-inline-end: 0.25rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 2 / 3;
|
||||
}
|
||||
|
||||
.button {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / 3;
|
||||
place-self: center;
|
||||
|
||||
font-size: 0.875rem !important;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.buttonIcon {
|
||||
font-size: 0.875rem !important;
|
||||
}
|
||||
|
||||
.outbid {
|
||||
color: var(--color-error);
|
||||
}
|
||||
136
src/components/left/main/panes/GiftAuctionPane.tsx
Normal file
136
src/components/left/main/panes/GiftAuctionPane.tsx
Normal file
@ -0,0 +1,136 @@
|
||||
import { memo, useMemo } from '@teact';
|
||||
import { getActions, withGlobal } from '../../../../global';
|
||||
|
||||
import type { ApiStarGiftAuctionState, ApiStarGiftAuctionStateActive } from '../../../../api/types';
|
||||
import type { GlobalState } from '../../../../global/types';
|
||||
|
||||
import buildClassName from '../../../../util/buildClassName';
|
||||
import { partition } from '../../../../util/iteratees';
|
||||
import { getBidAuctionPosition } from '../../../common/helpers/gifts';
|
||||
|
||||
import useLang from '../../../../hooks/useLang';
|
||||
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||
import useHeaderPane, { type PaneState } from '../../../middle/hooks/useHeaderPane';
|
||||
|
||||
import CustomEmoji from '../../../common/CustomEmoji';
|
||||
import Button from '../../../ui/Button';
|
||||
import TextTimer from '../../../ui/TextTimer';
|
||||
|
||||
import paneStyles from './ChatPane.module.scss';
|
||||
import styles from './GiftAuctionPane.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
canShow?: boolean;
|
||||
onPaneStateChange: (state: PaneState) => void;
|
||||
};
|
||||
|
||||
type StateProps = Pick<GlobalState, 'activeGiftAuctionIds' | 'giftAuctionByGiftId'>;
|
||||
|
||||
const GiftAuctionPane = ({
|
||||
canShow,
|
||||
activeGiftAuctionIds,
|
||||
giftAuctionByGiftId,
|
||||
onPaneStateChange,
|
||||
}: OwnProps & StateProps) => {
|
||||
const { openGiftAuctionBidModal, openActiveGiftAuctionsModal } = getActions();
|
||||
const isOpen = canShow && Boolean(activeGiftAuctionIds?.length);
|
||||
const lang = useLang();
|
||||
|
||||
const { ref, shouldRender } = useHeaderPane({
|
||||
isOpen,
|
||||
onStateChange: onPaneStateChange,
|
||||
withResizeObserver: true,
|
||||
});
|
||||
|
||||
const [activeAuctions, winningCount, outbidCount] = useMemo(() => {
|
||||
const auctions = activeGiftAuctionIds?.map((id) => giftAuctionByGiftId?.[id])
|
||||
.filter((auc): auc is ApiStarGiftAuctionState => (
|
||||
auc?.state.type === 'active' && Boolean(auc.userState.bidAmount)),
|
||||
);
|
||||
|
||||
if (!auctions) return [undefined, 0, 0];
|
||||
const [winning, outbid] = partition(auctions, (auction) => {
|
||||
const state = auction.state as ApiStarGiftAuctionStateActive;
|
||||
const position = getBidAuctionPosition(
|
||||
auction.userState.bidAmount!, auction.userState.bidDate!, state.bidLevels,
|
||||
);
|
||||
return position <= auction.gift.giftsPerRound!;
|
||||
});
|
||||
return [auctions, winning.length, outbid.length];
|
||||
}, [activeGiftAuctionIds, giftAuctionByGiftId]);
|
||||
const activeAuctionsCount = activeAuctions?.length || 0;
|
||||
const singleActiveAuction = activeAuctions?.length === 1 ? activeAuctions[0] : undefined;
|
||||
|
||||
function renderSubtitleText() {
|
||||
if (!winningCount && !outbidCount) return undefined;
|
||||
if (winningCount && !outbidCount) return lang('ChatListAuctionWinning');
|
||||
if (!winningCount && outbidCount) return lang('ChatListAuctionOutbid');
|
||||
return lang('ChatListAuctionMixed', { winCount: winningCount, outbidCount });
|
||||
}
|
||||
|
||||
const handleClick = useLastCallback(() => {
|
||||
if (!activeAuctions?.length) return;
|
||||
if (singleActiveAuction) {
|
||||
openGiftAuctionBidModal({ auctionGiftId: singleActiveAuction.gift.id });
|
||||
return;
|
||||
}
|
||||
|
||||
openActiveGiftAuctionsModal();
|
||||
});
|
||||
|
||||
if (!shouldRender) return undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={buildClassName(paneStyles.pane, styles.root)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className={buildClassName(paneStyles.title, styles.title)}>
|
||||
<span className={styles.giftEmojis}>
|
||||
{activeAuctions?.map((auction) => (
|
||||
<CustomEmoji
|
||||
key={auction.gift.id}
|
||||
sticker={auction.gift.sticker}
|
||||
loopLimit={1}
|
||||
/>
|
||||
))}
|
||||
</span>
|
||||
{lang('ChatListAuctionTitle', { count: activeAuctionsCount }, { pluralValue: activeAuctionsCount })}
|
||||
</div>
|
||||
<div
|
||||
className={buildClassName(
|
||||
paneStyles.subtitle, styles.subtitle, !winningCount && outbidCount && styles.outbid,
|
||||
)}
|
||||
>
|
||||
{renderSubtitleText()}
|
||||
</div>
|
||||
<Button
|
||||
className={styles.button}
|
||||
iconName={singleActiveAuction ? 'auction-filled' : undefined}
|
||||
iconClassName={styles.buttonIcon}
|
||||
size="tiny"
|
||||
pill
|
||||
onClick={handleClick}
|
||||
>
|
||||
{singleActiveAuction ? (
|
||||
<TextTimer
|
||||
endsAt={(singleActiveAuction.state as ApiStarGiftAuctionStateActive).nextRoundAt}
|
||||
shouldShowZeroOnEnd
|
||||
/>
|
||||
) : lang('ChatListAuctionView')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): Complete<StateProps> => {
|
||||
return {
|
||||
activeGiftAuctionIds: global.activeGiftAuctionIds,
|
||||
giftAuctionByGiftId: global.giftAuctionByGiftId,
|
||||
};
|
||||
},
|
||||
)(GiftAuctionPane));
|
||||
@ -1,43 +1,17 @@
|
||||
@use "../../../../styles/mixins";
|
||||
|
||||
.root {
|
||||
@include mixins.chat-list-pane;
|
||||
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
grid-template-rows: min-content 1fr;
|
||||
|
||||
border-radius: var(--border-radius-default);
|
||||
|
||||
line-height: 1.25;
|
||||
|
||||
background-color: var(--color-background-secondary);
|
||||
|
||||
&:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
--emoji-size: 1.125rem;
|
||||
--custom-emoji-size: var(--emoji-size);
|
||||
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 2;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
--emoji-size: 1rem;
|
||||
--custom-emoji-size: var(--emoji-size);
|
||||
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 2 / 3;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
|
||||
@ -4,6 +4,7 @@ import { getActions } from '../../../../global';
|
||||
import type { ApiPromoData } from '../../../../api/types';
|
||||
import type { RegularLangKey } from '../../../../types/language';
|
||||
|
||||
import buildClassName from '../../../../util/buildClassName';
|
||||
import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities';
|
||||
|
||||
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||
@ -13,6 +14,7 @@ import useHeaderPane, { type PaneState } from '../../../middle/hooks/useHeaderPa
|
||||
|
||||
import Icon from '../../../common/icons/Icon';
|
||||
|
||||
import paneStyles from './ChatPane.module.scss';
|
||||
import styles from './SuggestionPane.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
@ -90,13 +92,13 @@ const SuggestionPane = ({ promoData, onPaneStateChange }: OwnProps) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={styles.root}
|
||||
className={buildClassName(paneStyles.pane, styles.root)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<div className={styles.subtitle}>{message}</div>
|
||||
<div className={buildClassName(paneStyles.title, styles.title)}>{title}</div>
|
||||
<div className={buildClassName(paneStyles.subtitle, styles.subtitle)}>{message}</div>
|
||||
<Icon name="close" className={styles.closeIcon} onClick={handleDismiss} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -266,6 +266,7 @@ const Main = ({
|
||||
loadContentSettings,
|
||||
loadGiftAuction,
|
||||
loadPromoData,
|
||||
loadActiveGiftAuctions,
|
||||
} = getActions();
|
||||
|
||||
if (DEBUG && !DEBUG_isLogged) {
|
||||
@ -347,6 +348,7 @@ const Main = ({
|
||||
loadRestrictedEmojiStickers();
|
||||
loadQuickReplies();
|
||||
loadTimezones();
|
||||
loadActiveGiftAuctions();
|
||||
}
|
||||
}, [isMasterTab, isSynced, isAppConfigLoaded, isAccountFrozen]);
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ export interface PaneState {
|
||||
element?: HTMLElement;
|
||||
height: number;
|
||||
isOpen?: boolean;
|
||||
isSpacer?: boolean;
|
||||
}
|
||||
|
||||
// Max slide transition duration
|
||||
@ -130,18 +131,17 @@ export function applyAnimationState({
|
||||
list,
|
||||
noTransition = false,
|
||||
zIndexIncrease,
|
||||
topMargin = 0,
|
||||
}: {
|
||||
list: PaneState[];
|
||||
noTransition?: boolean;
|
||||
zIndexIncrease?: boolean;
|
||||
topMargin?: number;
|
||||
}) {
|
||||
let cumulativeHeight = 0;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const state = list[i];
|
||||
const element = state.element;
|
||||
if (!element) {
|
||||
if (state.isSpacer) cumulativeHeight += state.height;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ export function applyAnimationState({
|
||||
|
||||
const apply = () => {
|
||||
setExtraStyles(element, {
|
||||
transform: `translateY(${state.isOpen ? shiftPx : `calc(${shiftPx} - ${topMargin}px - 100%)`})`,
|
||||
transform: `translateY(${state.isOpen ? shiftPx : `calc(${shiftPx} - 100%)`})`,
|
||||
zIndex: String(-i),
|
||||
transition: noTransition ? 'none' : '',
|
||||
});
|
||||
@ -158,7 +158,7 @@ export function applyAnimationState({
|
||||
if (!element.dataset.isPanelOpen && state.isOpen && !noTransition) {
|
||||
// Start animation right above its final position
|
||||
setExtraStyles(element, {
|
||||
transform: `translateY(calc(${shiftPx} - ${topMargin}px - 100%))`,
|
||||
transform: `translateY(calc(${shiftPx} - 100%))`,
|
||||
zIndex: String(zIndexIncrease ? i : -i),
|
||||
transition: 'none',
|
||||
});
|
||||
|
||||
@ -20,6 +20,7 @@ import DeleteAccountModal from './deleteAccount/DeleteAccountModal.async';
|
||||
import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async';
|
||||
import FrozenAccountModal from './frozenAccount/FrozenAccountModal.async';
|
||||
import AboutStarGiftModal from './gift/AboutStarGiftModal.async';
|
||||
import ActiveGiftAuctionsModal from './gift/auction/ActiveGiftAuctionsModal.async';
|
||||
import GiftAuctionAcquiredModal from './gift/auction/GiftAuctionAcquiredModal.async';
|
||||
import GiftAuctionBidModal from './gift/auction/GiftAuctionBidModal.async';
|
||||
import GiftAuctionChangeRecipientModal from './gift/auction/GiftAuctionChangeRecipientModal.async';
|
||||
@ -107,6 +108,7 @@ type ModalKey = keyof Pick<TabState,
|
||||
'giftAuctionInfoModal' |
|
||||
'giftAuctionChangeRecipientModal' |
|
||||
'giftAuctionAcquiredModal' |
|
||||
'activeGiftAuctionsModal' |
|
||||
'starGiftPriceDecreaseInfoModal' |
|
||||
'aboutStarGiftModal' |
|
||||
'monetizationVerificationModal' |
|
||||
@ -182,6 +184,7 @@ const MODALS: ModalRegistry = {
|
||||
giftAuctionInfoModal: GiftAuctionInfoModal,
|
||||
giftAuctionChangeRecipientModal: GiftAuctionChangeRecipientModal,
|
||||
giftAuctionAcquiredModal: GiftAuctionAcquiredModal,
|
||||
activeGiftAuctionsModal: ActiveGiftAuctionsModal,
|
||||
starGiftPriceDecreaseInfoModal: StarGiftPriceDecreaseInfoModal,
|
||||
aboutStarGiftModal: AboutStarGiftModal,
|
||||
monetizationVerificationModal: VerificationMonetizationModal,
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import type { OwnProps } from './ActiveGiftAuctionsModal';
|
||||
|
||||
import { Bundles } from '../../../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||
|
||||
const ActiveGiftAuctionsModalAsync = (props: OwnProps) => {
|
||||
const { modal } = props;
|
||||
const ActiveGiftAuctionsModal = useModuleLoader(Bundles.Stars, 'ActiveGiftAuctionsModal', !modal);
|
||||
|
||||
return ActiveGiftAuctionsModal ? <ActiveGiftAuctionsModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default ActiveGiftAuctionsModalAsync;
|
||||
@ -0,0 +1,24 @@
|
||||
.gift {
|
||||
--custom-emoji-size: 2rem;
|
||||
|
||||
margin-inline-end: 0.5rem;
|
||||
}
|
||||
|
||||
.timer {
|
||||
margin-inline-start: 0.25rem;
|
||||
}
|
||||
|
||||
.noActive {
|
||||
color: var(--color-text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
--border-radius-default: 0;
|
||||
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
123
src/components/modals/gift/auction/ActiveGiftAuctionsModal.tsx
Normal file
123
src/components/modals/gift/auction/ActiveGiftAuctionsModal.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
import { memo, useMemo } from '@teact';
|
||||
import { getActions, withGlobal } from '../../../../global';
|
||||
|
||||
import type { ApiStarGiftAuctionState, ApiStarGiftAuctionStateActive } from '../../../../api/types';
|
||||
import type { TabState } from '../../../../global/types';
|
||||
|
||||
import { formatStarsAsIcon } from '../../../../util/localization/format';
|
||||
import { getBidAuctionPosition } from '../../../common/helpers/gifts';
|
||||
import { REM } from '../../../common/helpers/mediaDimensions';
|
||||
|
||||
import useLang from '../../../../hooks/useLang';
|
||||
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||
|
||||
import CustomEmoji from '../../../common/CustomEmoji';
|
||||
import Button from '../../../ui/Button';
|
||||
import ListItem from '../../../ui/ListItem';
|
||||
import Modal from '../../../ui/Modal';
|
||||
import TextTimer from '../../../ui/TextTimer';
|
||||
|
||||
import styles from './ActiveGiftAuctionsModal.module.scss';
|
||||
|
||||
export type OwnProps = {
|
||||
modal: TabState['activeGiftAuctionsModal'];
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
activeGiftAuctionIds?: string[];
|
||||
giftAuctionByGiftId?: Record<string, ApiStarGiftAuctionState>;
|
||||
};
|
||||
|
||||
const ICON_SIZE = 2 * REM;
|
||||
|
||||
const ActiveGiftAuctionsModal = ({
|
||||
modal, activeGiftAuctionIds, giftAuctionByGiftId,
|
||||
}: OwnProps & StateProps) => {
|
||||
const { closeActiveGiftAuctionsModal } = getActions();
|
||||
const lang = useLang();
|
||||
|
||||
const activeAuctions = useMemo(() => {
|
||||
return activeGiftAuctionIds?.map((id) => giftAuctionByGiftId?.[id])
|
||||
.filter((auc): auc is ApiStarGiftAuctionState => (
|
||||
auc?.state.type === 'active' && Boolean(auc.userState.bidAmount)
|
||||
));
|
||||
}, [activeGiftAuctionIds, giftAuctionByGiftId]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={Boolean(modal)}
|
||||
onClose={closeActiveGiftAuctionsModal}
|
||||
hasCloseButton
|
||||
isCondensedHeader
|
||||
title={lang('GiftAuctionActiveTitle')}
|
||||
dialogClassName={styles.dialog}
|
||||
contentClassName={styles.content}
|
||||
>
|
||||
{activeAuctions?.length
|
||||
? activeAuctions.map((auction) => <ActiveAuctionItem key={auction.gift.id} auction={auction} />)
|
||||
: <div className={styles.noActive}>{lang('GiftAuctionNoActive')}</div>}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
function ActiveAuctionItem({ auction }: { auction: ApiStarGiftAuctionState }) {
|
||||
const lang = useLang();
|
||||
const { openGiftAuctionBidModal, closeActiveGiftAuctionsModal } = getActions();
|
||||
|
||||
const { userState, gift } = auction;
|
||||
const state = auction.state as ApiStarGiftAuctionStateActive;
|
||||
|
||||
const bidPosition = useMemo(() => {
|
||||
return getBidAuctionPosition(userState.bidAmount!, userState.bidDate!, state.bidLevels);
|
||||
}, [userState, state.bidLevels]);
|
||||
|
||||
const handleClick = useLastCallback(() => {
|
||||
openGiftAuctionBidModal({ auctionGiftId: gift.id });
|
||||
closeActiveGiftAuctionsModal();
|
||||
});
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
leftElement={<CustomEmoji className={styles.gift} sticker={gift.sticker} size={ICON_SIZE} loopLimit={1} />}
|
||||
rightElement={(
|
||||
<Button
|
||||
size="tiny"
|
||||
pill
|
||||
fluid
|
||||
iconName="auction-filled"
|
||||
onClick={handleClick}
|
||||
>
|
||||
{lang('GiftAuctionListRaiseBid')}
|
||||
<TextTimer className={styles.timer} endsAt={state.nextRoundAt} shouldShowZeroOnEnd />
|
||||
</Button>
|
||||
)}
|
||||
multiline
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className="title">
|
||||
{lang('GiftAuctionListRound', {
|
||||
current: lang.number(state.currentRound),
|
||||
total: lang.number(state.totalRounds),
|
||||
})}
|
||||
</div>
|
||||
<div className="subtitle">
|
||||
{lang('GiftAuctionBidPosition', {
|
||||
amount: formatStarsAsIcon(lang, userState.bidAmount!, { noStyles: true }),
|
||||
position: lang.number(bidPosition),
|
||||
}, {
|
||||
withNodes: true,
|
||||
})}
|
||||
</div>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): Complete<StateProps> => {
|
||||
const { activeGiftAuctionIds, giftAuctionByGiftId } = global;
|
||||
return {
|
||||
activeGiftAuctionIds,
|
||||
giftAuctionByGiftId,
|
||||
};
|
||||
},
|
||||
)(ActiveGiftAuctionsModal));
|
||||
@ -10,6 +10,7 @@ import type { TabState } from '../../../../global/types';
|
||||
|
||||
import { selectPeer, selectTabState } from '../../../../global/selectors';
|
||||
import { formatStarsAsIcon } from '../../../../util/localization/format';
|
||||
import { getBidAuctionPosition } from '../../../common/helpers/gifts';
|
||||
import renderText from '../../../common/helpers/renderText';
|
||||
|
||||
import { useTransitionActiveKey } from '../../../../hooks/animations/useTransitionActiveKey';
|
||||
@ -191,14 +192,7 @@ const GiftAuctionBidModal = ({
|
||||
const { bidLevels } = activeState;
|
||||
const userBidDate = userState?.bidDate || Number.MAX_SAFE_INTEGER;
|
||||
|
||||
for (const level of bidLevels) {
|
||||
if (level.amount < selectedBidAmount
|
||||
|| (level.amount === selectedBidAmount && level.date >= userBidDate)) {
|
||||
return level.pos;
|
||||
}
|
||||
}
|
||||
|
||||
return bidLevels[bidLevels.length - 1].pos + 1;
|
||||
return getBidAuctionPosition(selectedBidAmount, userBidDate, bidLevels);
|
||||
}, [selectedBidAmount, activeState, userState?.bidDate]);
|
||||
|
||||
function renderInfoCards() {
|
||||
|
||||
@ -112,7 +112,6 @@ const GiftTransferModal = ({
|
||||
hasCloseButton
|
||||
shouldAdaptToSearch
|
||||
withFixedHeight
|
||||
ignoreFreeze
|
||||
>
|
||||
<PeerPicker<Categories>
|
||||
itemIds={displayIds}
|
||||
|
||||
@ -370,8 +370,8 @@
|
||||
font-size: 0.875rem;
|
||||
|
||||
&.round {
|
||||
width: 1.875rem;
|
||||
height: 1.875rem;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@ -380,8 +380,8 @@
|
||||
}
|
||||
|
||||
&.pill {
|
||||
height: 1.875rem;
|
||||
padding: 0.3125rem 1rem;
|
||||
height: 1.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@ -45,7 +45,6 @@ export type OwnProps = {
|
||||
dialogRef?: ElementRef<HTMLDivElement>;
|
||||
isLowStackPriority?: boolean;
|
||||
dialogContent?: React.ReactNode;
|
||||
ignoreFreeze?: boolean;
|
||||
moreMenuItems?: TeactNode;
|
||||
onClose: () => void;
|
||||
onCloseAnimationEnd?: () => void;
|
||||
|
||||
@ -9,6 +9,7 @@ import useForceUpdate from '../../hooks/useForceUpdate';
|
||||
import AnimatedCounter from '../common/AnimatedCounter';
|
||||
|
||||
type OwnProps = {
|
||||
className?: string;
|
||||
endsAt: number;
|
||||
shouldShowZeroOnEnd?: boolean;
|
||||
onEnd?: NoneToVoidFunction;
|
||||
@ -16,7 +17,7 @@ type OwnProps = {
|
||||
|
||||
const UPDATE_FREQUENCY = 500; // Sometimes second gets skipped if using 1000
|
||||
|
||||
const TextTimer = ({ endsAt, shouldShowZeroOnEnd, onEnd }: OwnProps) => {
|
||||
const TextTimer = ({ className, endsAt, shouldShowZeroOnEnd, onEnd }: OwnProps) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const serverTime = getServerTime();
|
||||
@ -35,8 +36,8 @@ const TextTimer = ({ endsAt, shouldShowZeroOnEnd, onEnd }: OwnProps) => {
|
||||
const time = formatMediaDuration(timeLeft);
|
||||
|
||||
const timeParts = time.split(':');
|
||||
const timeCounter = (
|
||||
<span style="font-variant-numeric: tabular-nums;">
|
||||
return (
|
||||
<span className={className} style="font-variant-numeric: tabular-nums;">
|
||||
{timeParts.map((part, index) => (
|
||||
<>
|
||||
{index > 0 && ':'}
|
||||
@ -45,12 +46,6 @@ const TextTimer = ({ endsAt, shouldShowZeroOnEnd, onEnd }: OwnProps) => {
|
||||
))}
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<span>
|
||||
{timeCounter}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextTimer;
|
||||
|
||||
@ -612,6 +612,7 @@ addActionHandler('loadGiftAuction', async (global, _actions, payload): Promise<v
|
||||
|
||||
global = getGlobal();
|
||||
global = replaceGiftAuction(global, auctionState);
|
||||
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
@ -735,3 +736,19 @@ addActionHandler('declineStarGiftOffer', async (global, actions, payload): Promi
|
||||
shouldDecline: true,
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('loadActiveGiftAuctions', async (global, actions, payload): Promise<void> => {
|
||||
const result = await callApi('fetchStarGiftActiveAuctions');
|
||||
|
||||
if (!result) return;
|
||||
|
||||
global = getGlobal();
|
||||
result.auctions.forEach((auction) => {
|
||||
global = replaceGiftAuction(global, auction);
|
||||
});
|
||||
global = {
|
||||
...global,
|
||||
activeGiftAuctionIds: result.auctions.map((auction) => auction.gift.id),
|
||||
};
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
@ -260,6 +260,11 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
case 'updateStarGiftAuctionUserState': {
|
||||
const { giftId, userState } = update;
|
||||
|
||||
if (!global.giftAuctionByGiftId?.[giftId]) {
|
||||
actions.loadActiveGiftAuctions();
|
||||
return;
|
||||
}
|
||||
|
||||
global = updateGiftAuctionUserState(global, giftId, userState);
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
@ -676,3 +676,13 @@ addActionHandler('resetSelectedGiftCollection', (global, actions, payload): Acti
|
||||
peerId, shouldRefresh: true, tabId: tabState.id,
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('openActiveGiftAuctionsModal', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
|
||||
return updateTabState(global, {
|
||||
activeGiftAuctionsModal: true,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addTabStateResetterAction('closeActiveGiftAuctionsModal', 'activeGiftAuctionsModal');
|
||||
|
||||
@ -7,7 +7,7 @@ import type {
|
||||
import type { GlobalState } from '../types';
|
||||
|
||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||
import { omit } from '../../util/iteratees';
|
||||
import { omit, unique } from '../../util/iteratees';
|
||||
import { updateTabState } from './tabs';
|
||||
|
||||
export function removeGiftInfoOriginalDetails<T extends GlobalState>(
|
||||
@ -74,7 +74,11 @@ export function updateGiftAuction<T extends GlobalState>(
|
||||
...global,
|
||||
giftAuctionByGiftId: {
|
||||
...global.giftAuctionByGiftId,
|
||||
[giftId]: auctionState,
|
||||
[giftId]: {
|
||||
...auctionState,
|
||||
// Keep timeout in case of updates from active auction list, as those do not have this field
|
||||
timeout: auctionState.timeout || currentAuction.timeout,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -123,6 +127,21 @@ export function updateGiftAuctionUserState<T extends GlobalState>(
|
||||
return global;
|
||||
}
|
||||
|
||||
const currentBidDate = giftAuction.userState.bidDate;
|
||||
if (userState.bidDate !== currentBidDate) {
|
||||
if (userState.bidDate) {
|
||||
global = {
|
||||
...global,
|
||||
activeGiftAuctionIds: unique([...(global.activeGiftAuctionIds || []), giftId]),
|
||||
};
|
||||
} else {
|
||||
global = {
|
||||
...global,
|
||||
activeGiftAuctionIds: global.activeGiftAuctionIds?.filter((id) => id !== giftId),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...global,
|
||||
giftAuctionByGiftId: {
|
||||
@ -140,11 +159,32 @@ export function replaceGiftAuction<T extends GlobalState>(
|
||||
auctionState: ApiStarGiftAuctionState,
|
||||
): T {
|
||||
const giftId = auctionState.gift.id;
|
||||
|
||||
const previousAuction = global.giftAuctionByGiftId?.[giftId];
|
||||
if (previousAuction) {
|
||||
if (previousAuction.userState.bidDate !== auctionState.userState.bidDate) {
|
||||
if (auctionState.userState.bidDate) {
|
||||
global = {
|
||||
...global,
|
||||
activeGiftAuctionIds: unique([...(global.activeGiftAuctionIds || []), giftId]),
|
||||
};
|
||||
} else {
|
||||
global = {
|
||||
...global,
|
||||
activeGiftAuctionIds: global.activeGiftAuctionIds?.filter((id) => id !== giftId),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...global,
|
||||
giftAuctionByGiftId: {
|
||||
...global.giftAuctionByGiftId,
|
||||
[giftId]: auctionState,
|
||||
[giftId]: {
|
||||
...auctionState,
|
||||
timeout: auctionState.timeout || previousAuction?.timeout,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -158,9 +198,11 @@ export function removeGiftAuction<T extends GlobalState>(
|
||||
}
|
||||
|
||||
const rest = omit(global.giftAuctionByGiftId, [giftId]);
|
||||
const activeGiftAuctionIds = global.activeGiftAuctionIds?.filter((id) => id !== giftId);
|
||||
|
||||
return {
|
||||
...global,
|
||||
giftAuctionByGiftId: Object.keys(rest).length ? rest : undefined,
|
||||
activeGiftAuctionIds,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2714,6 +2714,9 @@ export interface ActionPayloads {
|
||||
gift: ApiStarGiftUnique;
|
||||
} & WithTabId;
|
||||
closeGiftInfoValueModal: WithTabId | undefined;
|
||||
loadActiveGiftAuctions: undefined;
|
||||
openActiveGiftAuctionsModal: WithTabId | undefined;
|
||||
closeActiveGiftAuctionsModal: WithTabId | undefined;
|
||||
openGiftAuctionModal: {
|
||||
gift: ApiStarGiftRegular;
|
||||
} & WithTabId;
|
||||
|
||||
@ -331,6 +331,7 @@ export type GlobalState = {
|
||||
starGiftCollections?: {
|
||||
byPeerId: Record<string, ApiStarGiftCollection[]>;
|
||||
};
|
||||
activeGiftAuctionIds?: string[];
|
||||
giftAuctionByGiftId?: Record<string, ApiStarGiftAuctionState>;
|
||||
|
||||
stickers: {
|
||||
|
||||
@ -937,6 +937,8 @@ export type TabState = {
|
||||
acquiredGifts?: ApiStarGiftAuctionAcquiredGift[];
|
||||
};
|
||||
|
||||
activeGiftAuctionsModal?: true;
|
||||
|
||||
starGiftPriceDecreaseInfoModal?: {
|
||||
prices: ApiStarGiftUpgradePrice[];
|
||||
currentPrice: number;
|
||||
|
||||
@ -1895,6 +1895,7 @@ payments.getUniqueStarGiftValueInfo#4365af6b slug:string = payments.UniqueStarGi
|
||||
payments.checkCanSendGift#c0c4edc9 gift_id:long = payments.CheckCanSendGiftResult;
|
||||
payments.getStarGiftAuctionState#5c9ff4d6 auction:InputStarGiftAuction version:int = payments.StarGiftAuctionState;
|
||||
payments.getStarGiftAuctionAcquiredGifts#6ba2cbec gift_id:long = payments.StarGiftAuctionAcquiredGifts;
|
||||
payments.getStarGiftActiveAuctions#a5d0514d hash:long = payments.StarGiftActiveAuctions;
|
||||
payments.resolveStarGiftOffer#e9ce781c flags:# decline:flags.0?true offer_msg_id:int = Updates;
|
||||
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
|
||||
@ -355,6 +355,7 @@
|
||||
"payments.getStarGiftCollections",
|
||||
"payments.getStarGiftAuctionState",
|
||||
"payments.getStarGiftAuctionAcquiredGifts",
|
||||
"payments.getStarGiftActiveAuctions",
|
||||
"payments.resolveStarGiftOffer",
|
||||
"langpack.getLangPack",
|
||||
"langpack.getStrings",
|
||||
|
||||
21
src/types/language.d.ts
vendored
21
src/types/language.d.ts
vendored
@ -1844,6 +1844,9 @@ export interface LangPair {
|
||||
'GiftAuctionTapToBidMore': undefined;
|
||||
'GiftAuctionLearnMoreAboutGifts': undefined;
|
||||
'GiftAuctionLearnMoreMenuItem': undefined;
|
||||
'GiftAuctionListRaiseBid': undefined;
|
||||
'GiftAuctionActiveTitle': undefined;
|
||||
'GiftAuctionNoActive': undefined;
|
||||
'StarGiftInfoTitle': undefined;
|
||||
'StarGiftInfoSubtitle': undefined;
|
||||
'StarGiftInfoUniqueTitle': undefined;
|
||||
@ -1913,6 +1916,9 @@ export interface LangPair {
|
||||
'SettingsDataClearMediaCache': undefined;
|
||||
'SettingsDataClearMediaCacheDescription': undefined;
|
||||
'SettingsDataClearMediaDone': undefined;
|
||||
'ChatListAuctionWinning': undefined;
|
||||
'ChatListAuctionOutbid': undefined;
|
||||
'ChatListAuctionView': undefined;
|
||||
}
|
||||
|
||||
export interface LangPairWithVariables<V = LangVariable> {
|
||||
@ -3312,6 +3318,14 @@ export interface LangPairWithVariables<V = LangVariable> {
|
||||
'GiftAuctionWonNotification': {
|
||||
'gift': V;
|
||||
};
|
||||
'GiftAuctionBidPosition': {
|
||||
'amount': V;
|
||||
'position': V;
|
||||
};
|
||||
'GiftAuctionListRound': {
|
||||
'current': V;
|
||||
'total': V;
|
||||
};
|
||||
'SettingsPasskeyUsedAt': {
|
||||
'date': V;
|
||||
};
|
||||
@ -3346,6 +3360,10 @@ export interface LangPairWithVariables<V = LangVariable> {
|
||||
'status': V;
|
||||
'onlineCount': V;
|
||||
};
|
||||
'ChatListAuctionMixed': {
|
||||
'winCount': V;
|
||||
'outbidCount': V;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LangPairPlural {
|
||||
@ -3785,6 +3803,9 @@ export interface LangPairPluralWithVariables<V = LangVariable> {
|
||||
'AttachmentSendFile': {
|
||||
'count': V;
|
||||
};
|
||||
'ChatListAuctionTitle': {
|
||||
'count': V;
|
||||
};
|
||||
}
|
||||
export type RegularLangKey = keyof LangPair;
|
||||
export type RegularLangKeyWithVariables = keyof LangPairWithVariables;
|
||||
|
||||
@ -49,11 +49,15 @@ export function formatTonAsIcon(
|
||||
}
|
||||
|
||||
export function formatStarsAsIcon(lang: LangFn, amount: number | string, options?: {
|
||||
asFont?: boolean; className?: string; containerClassName?: string; }) {
|
||||
const { asFont, className, containerClassName } = options || {};
|
||||
asFont?: boolean;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
noStyles?: boolean;
|
||||
}) {
|
||||
const { asFont, className, containerClassName, noStyles } = options || {};
|
||||
const icon = asFont
|
||||
? <Icon name="star" className={buildClassName('star-amount-icon', className)} />
|
||||
: <StarIcon type="gold" className={buildClassName('star-amount-icon', className)} size="adaptive" />;
|
||||
? <Icon name="star" className={buildClassName(!noStyles && 'star-amount-icon', className)} />
|
||||
: <StarIcon type="gold" className={buildClassName(!noStyles && 'star-amount-icon', className)} size="adaptive" />;
|
||||
|
||||
if (containerClassName) {
|
||||
return (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user