diff --git a/src/api/gramjs/apiBuilders/payments.ts b/src/api/gramjs/apiBuilders/payments.ts index 679ce7eb5..03dd48f27 100644 --- a/src/api/gramjs/apiBuilders/payments.ts +++ b/src/api/gramjs/apiBuilders/payments.ts @@ -2,6 +2,7 @@ import { Api as GramJs } from '../../../lib/gramjs'; import type { ApiPremiumSection } from '../../../global/types'; import type { + ApiBoost, ApiBoostsStatus, ApiCheckedGiftCode, ApiGiveawayInfo, @@ -223,6 +224,24 @@ export function buildApiBoostsStatus(boostStatus: GramJs.premium.BoostsStatus): }; } +export function buildApiBoost(boost: GramJs.Boost): ApiBoost { + const { + userId, + multiplier, + expires, + giveaway, + gift, + } = boost; + + return { + userId: userId && buildApiPeerId(userId, 'user'), + multiplier, + expires, + isFromGiveaway: giveaway, + isGift: gift, + }; +} + export function buildApiMyBoost(myBoost: GramJs.MyBoost): ApiMyBoost { const { date, expires, slot, cooldownUntilDate, peer, diff --git a/src/api/gramjs/methods/payments.ts b/src/api/gramjs/methods/payments.ts index 815c13c8d..ceba4f0bb 100644 --- a/src/api/gramjs/methods/payments.ts +++ b/src/api/gramjs/methods/payments.ts @@ -6,9 +6,9 @@ import type { OnApiUpdate, } from '../../types'; -import { buildCollectionByCallback } from '../../../util/iteratees'; import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { + buildApiBoost, buildApiBoostsStatus, buildApiCheckedGiftCode, buildApiGiveawayInfo, @@ -266,15 +266,18 @@ export async function fetchBoostStatus({ export async function fetchBoostList({ chat, + isGifts, offset = '', limit, }: { chat: ApiChat; + isGifts?: boolean; offset?: string; limit?: number; }) { const result = await invokeRequest(new GramJs.premium.GetBoostsList({ peer: buildInputPeer(chat.id, chat.accessHash), + gifts: isGifts || undefined, offset, limit, })); @@ -287,17 +290,12 @@ export async function fetchBoostList({ const users = result.users.map(buildApiUser).filter(Boolean); - const userBoosts = result.boosts.filter((boost) => boost.userId); - const boosterIds = userBoosts.map((boost) => boost.userId!.toString()); - const boosters = buildCollectionByCallback(userBoosts, (boost) => ( - [boost.userId!.toString(), boost.expires] - )); + const boostList = result.boosts.map(buildApiBoost); return { count: result.count, + boostList, users, - boosters, - boosterIds, nextOffset: result.nextOffset, }; } diff --git a/src/api/types/payments.ts b/src/api/types/payments.ts index 2b28a9e11..4b7bf9637 100644 --- a/src/api/types/payments.ts +++ b/src/api/types/payments.ts @@ -132,6 +132,14 @@ export type ApiMyBoost = { cooldownUntil?: number; }; +export type ApiBoost = { + userId?: string; + multiplier?: number; + expires: number; + isFromGiveaway?: boolean; + isGift?: boolean; +}; + export type ApiGiveawayInfoActive = { type: 'active'; isParticipating?: true; diff --git a/src/components/common/Avatar.scss b/src/components/common/Avatar.scss index 1e0229313..1ca79c71f 100644 --- a/src/components/common/Avatar.scss +++ b/src/components/common/Avatar.scss @@ -257,4 +257,8 @@ &.hidden-user { --color-user: var(--color-deleted-account); } + + &.unknown-user { + background: var(--premium-gradient); + } } diff --git a/src/components/common/Avatar.tsx b/src/components/common/Avatar.tsx index b1879ef6b..53a77d162 100644 --- a/src/components/common/Avatar.tsx +++ b/src/components/common/Avatar.tsx @@ -53,6 +53,7 @@ type OwnProps = { photo?: ApiPhoto; text?: string; isSavedMessages?: boolean; + isUnknownUser?: boolean; isSavedDialog?: boolean; withVideo?: boolean; withStory?: boolean; @@ -77,6 +78,7 @@ const Avatar: FC = ({ text, isSavedMessages, isSavedDialog, + isUnknownUser, withVideo, withStory, forPremiumPromo, @@ -120,6 +122,10 @@ const Avatar: FC = ({ } const specialIcon = useMemo(() => { + if (isUnknownUser) { + return 'user'; + } + if (isSavedMessages) { return isSavedDialog ? 'my-notes' : 'avatar-saved-messages'; } @@ -137,7 +143,7 @@ const Avatar: FC = ({ } return undefined; - }, [isAnonymousForwards, isDeleted, isSavedDialog, isReplies, isSavedMessages]); + }, [isUnknownUser, isSavedMessages, isDeleted, isReplies, isAnonymousForwards, isSavedDialog]); const imgBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl); const videoBlobUrl = useMedia(videoHash, !shouldLoadVideo, ApiMediaFormat.BlobUrl); @@ -215,6 +221,7 @@ const Avatar: FC = ({ `Avatar size-${size}`, className, getPeerColorClass(peer), + isUnknownUser && 'unknown-user', !peer && text && 'hidden-user', isSavedMessages && 'saved-messages', isAnonymousForwards && 'anonymous-forwards', diff --git a/src/components/common/FullNameTitle.tsx b/src/components/common/FullNameTitle.tsx index 2f6ecb414..7f335590f 100644 --- a/src/components/common/FullNameTitle.tsx +++ b/src/components/common/FullNameTitle.tsx @@ -25,7 +25,7 @@ import VerifiedIcon from './VerifiedIcon'; import styles from './FullNameTitle.module.scss'; type OwnProps = { - peer: ApiPeer; + peer?: ApiPeer; className?: string; noVerified?: boolean; noFake?: boolean; @@ -34,9 +34,11 @@ type OwnProps = { isSavedMessages?: boolean; isSavedDialog?: boolean; noLoopLimit?: boolean; + isUnknownUser?: boolean; canCopyTitle?: boolean; onEmojiStatusClick?: NoneToVoidFunction; observeIntersection?: ObserveFn; + iconElement?: React.ReactNode; }; const FullNameTitle: FC = ({ @@ -52,13 +54,26 @@ const FullNameTitle: FC = ({ canCopyTitle, onEmojiStatusClick, observeIntersection, + iconElement, + isUnknownUser, }) => { const lang = useLang(); const { showNotification } = getActions(); - const isUser = isUserId(peer.id); - const title = isUser ? getUserFullName(peer as ApiUser) : getChatTitle(lang, peer as ApiChat); + const isUser = peer && isUserId(peer.id); const isPremium = isUser && (peer as ApiUser).isPremium; + const title = useMemo(() => { + if (isUnknownUser) { + return lang('BoostingToBeDistributed'); + } + + if (peer && isUserId(peer.id)) { + return getUserFullName(peer as ApiUser); + } + + return peer && getChatTitle(lang, peer as ApiChat); + }, [isUnknownUser, lang, peer]); + const handleTitleClick = useLastCallback((e) => { if (!title || !canCopyTitle) { return; @@ -74,16 +89,16 @@ const FullNameTitle: FC = ({ return lang(isSavedDialog ? 'MyNotes' : 'SavedMessages'); } - if (isAnonymousForwardsChat(peer.id)) { + if (peer && isAnonymousForwardsChat(peer.id)) { return lang('AnonymousForward'); } - if (isChatWithRepliesBot(peer.id)) { + if (peer && isChatWithRepliesBot(peer.id)) { return lang('RepliesTitle'); } return undefined; - }, [isSavedDialog, isSavedMessages, lang, peer.id]); + }, [isSavedDialog, isSavedMessages, lang, peer]); if (specialTitle) { return ( @@ -103,18 +118,23 @@ const FullNameTitle: FC = ({ > {renderText(title || '')} - {!noVerified && peer.isVerified && } - {!noFake && peer.fakeType && } - {withEmojiStatus && peer.emojiStatus && ( - + {!iconElement && peer && ( + <> + {!noVerified && peer?.isVerified && } + {!noFake && peer?.fakeType && } + {withEmojiStatus && peer.emojiStatus && ( + + )} + {withEmojiStatus && !peer.emojiStatus && isPremium && } + )} - {withEmojiStatus && !peer.emojiStatus && isPremium && } + {iconElement} ); }; diff --git a/src/components/common/PremiumProgress.module.scss b/src/components/common/PremiumProgress.module.scss index 108d24479..da3e31b39 100644 --- a/src/components/common/PremiumProgress.module.scss +++ b/src/components/common/PremiumProgress.module.scss @@ -3,9 +3,8 @@ display: flex; position: relative; height: 2rem; - background: #F1F3F5; + background: var(--color-background-menu-separator); border-radius: 0.625rem; - color: black; } .withBadge { diff --git a/src/components/common/PrivateChatInfo.tsx b/src/components/common/PrivateChatInfo.tsx index 50838e05d..3515582b0 100644 --- a/src/components/common/PrivateChatInfo.tsx +++ b/src/components/common/PrivateChatInfo.tsx @@ -9,7 +9,9 @@ import type { StoryViewerOrigin } from '../../types'; import type { IconName } from '../../types/icons'; import { MediaViewerOrigin } from '../../types'; -import { getMainUsername, getUserStatus, isUserOnline } from '../../global/helpers'; +import { + getMainUsername, getUserStatus, isUserOnline, +} from '../../global/helpers'; import { selectChatMessages, selectUser, selectUserStatus } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import renderText from './helpers/renderText'; @@ -36,10 +38,13 @@ type OwnProps = { withMediaViewer?: boolean; withUsername?: boolean; withStory?: boolean; + isUnknownUser?: boolean; withFullInfo?: boolean; withUpdatingStatus?: boolean; storyViewerOrigin?: StoryViewerOrigin; noEmojiStatus?: boolean; + noFake?: boolean; + noVerified?: boolean; emojiStatusSize?: number; noStatusOrTyping?: boolean; noRtl?: boolean; @@ -47,6 +52,8 @@ type OwnProps = { isSavedDialog?: boolean; className?: string; onEmojiStatusClick?: NoneToVoidFunction; + iconElement?: React.ReactNode; + rightElement?: React.ReactNode; }; type StateProps = @@ -73,6 +80,9 @@ const PrivateChatInfo: FC = ({ emojiStatusSize, noStatusOrTyping, noEmojiStatus, + noFake, + noVerified, + isUnknownUser, noRtl, user, userStatus, @@ -86,6 +96,8 @@ const PrivateChatInfo: FC = ({ storyViewerOrigin, isSynced, onEmojiStatusClick, + iconElement, + rightElement, }) => { const { loadFullUser, @@ -119,7 +131,7 @@ const PrivateChatInfo: FC = ({ const mainUsername = useMemo(() => user && withUsername && getMainUsername(user), [user, withUsername]); - if (!user) { + if (!user && !isUnknownUser) { return undefined; } @@ -183,11 +195,15 @@ const PrivateChatInfo: FC = ({ return ( ); } @@ -204,11 +220,12 @@ const PrivateChatInfo: FC = ({ /> )} = ({ {(status || (!isSavedMessages && !noStatusOrTyping)) && renderStatusOrTyping()} {ripple && } + {rightElement} ); }; diff --git a/src/components/payment/CardInput.tsx b/src/components/payment/CardInput.tsx index 90064ea61..df8e43b36 100644 --- a/src/components/payment/CardInput.tsx +++ b/src/components/payment/CardInput.tsx @@ -18,7 +18,7 @@ import mastercardIconPath from '../../assets/mastercard.svg'; import mirIconPath from '../../assets/mir.svg'; import visaIconPath from '../../assets/visa.svg'; -const CARD_NUMBER_MAX_LENGTH = 23; +const CARD_NUMBER_MAX_LENGTH = 19; export type OwnProps = { value: string; diff --git a/src/components/payment/PaymentModal.tsx b/src/components/payment/PaymentModal.tsx index 19a5ef42c..4ca09ea62 100644 --- a/src/components/payment/PaymentModal.tsx +++ b/src/components/payment/PaymentModal.tsx @@ -529,7 +529,7 @@ const PaymentModal: FC = ({ switch (step) { case PaymentStep.Checkout: - return Boolean(invoice?.isRecurring && !isTosAccepted); + return !isTosAccepted; case PaymentStep.PaymentInfo: return Boolean( diff --git a/src/components/right/statistics/BoostStatistics.module.scss b/src/components/right/statistics/BoostStatistics.module.scss index 7e9d16cee..6f111eba1 100644 --- a/src/components/right/statistics/BoostStatistics.module.scss +++ b/src/components/right/statistics/BoostStatistics.module.scss @@ -9,6 +9,7 @@ color: var(--color-text-secondary); text-align: center; font-size: 0.9375rem; + padding: 0.75rem 0 0 0; } .section-header { @@ -17,7 +18,7 @@ } .section { - padding: 1rem; + padding: 0.625rem; @include mixins.side-panel-section; } @@ -72,18 +73,36 @@ } .floatingBadgeButtonColor { - padding: 0.25rem 0.75rem 0.375rem 0.5625rem; + padding: 0 0.5rem 0 0.375rem; border-radius: 1rem; background-color: var(--color-primary-opacity); margin-left: 0.5rem; } +.floatingBadgeWarning { + color: var(--color-orange); + background: var( --color-light-coral); +} + +.floatingBadgeButton { + padding: 0.125rem 0.75rem 0.125rem 0.5625rem; +} + .floatingBadgeIcon { - font-size: 1.125rem; - margin-right: 0.1875rem; + font-size: 0.875rem; + margin-right: 0.125rem; } .floatingBadgeValue { font-size: 0.875rem; font-weight: 500; } + +.content { + padding: 0.5rem 0; +} + +.boostSection { + display: flex; + flex-direction: column-reverse; +} diff --git a/src/components/right/statistics/BoostStatistics.tsx b/src/components/right/statistics/BoostStatistics.tsx index 6ff1b4373..b9a6dac83 100644 --- a/src/components/right/statistics/BoostStatistics.tsx +++ b/src/components/right/statistics/BoostStatistics.tsx @@ -1,12 +1,20 @@ -import React, { memo, useMemo } from '../../../lib/teact/teact'; +import React, { + memo, useMemo, useRef, useState, +} from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiBoostStatistics, ApiPrepaidGiveaway } from '../../../api/types'; import type { TabState } from '../../../global/types'; -import { GIVEAWAY_BOOST_PER_PREMIUM } from '../../../config'; +import { + GIVEAWAY_BOOST_PER_PREMIUM, +} from '../../../config'; import { isChatChannel } from '../../../global/helpers'; -import { selectChat, selectIsGiveawayGiftsPurchaseAvailable, selectTabState } from '../../../global/selectors'; +import { + selectChat, + selectIsGiveawayGiftsPurchaseAvailable, + selectTabState, +} from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import { formatDateAtTime } from '../../../util/date/dateFormat'; import { getBoostProgressInfo } from '../../common/helpers/boostInfo'; @@ -21,6 +29,8 @@ import PrivateChatInfo from '../../common/PrivateChatInfo'; import ListItem from '../../ui/ListItem'; import Loading from '../../ui/Loading'; import Spinner from '../../ui/Spinner'; +import TabList from '../../ui/TabList'; +import Transition from '../../ui/Transition'; import StatisticsOverview from './StatisticsOverview'; import styles from './BoostStatistics.module.scss'; @@ -51,13 +61,20 @@ const BoostStatistics = ({ isChannel, }: StateProps) => { const { - openChat, loadMoreBoosters, closeBoostStatistics, openGiveawayModal, + openChat, loadMoreBoosters, closeBoostStatistics, openGiveawayModal, showNotification, } = getActions(); const lang = useLang(); + // eslint-disable-next-line no-null/no-null + const transitionRef = useRef(null); const isLoaded = boostStatistics?.boostStatus; const status = isLoaded ? boostStatistics.boostStatus : undefined; + const isGiftListEqual = boostStatistics && boostStatistics?.boosts?.count + === boostStatistics?.giftedBoosts?.count; + const shouldDisplayGiftList = !isGiftListEqual && boostStatistics?.giftedBoosts + && boostStatistics?.giftedBoosts?.list?.length > 0; + const { currentLevel, hasNextLevel, @@ -90,30 +107,140 @@ const BoostStatistics = ({ } satisfies ApiBoostStatistics; }, [status, boosts, currentLevel, remainingBoosts]); - const boostersToLoadCount = useMemo(() => { - if (!boostStatistics?.count) return undefined; - const loadedCount = boostStatistics.boosterIds?.length || 0; - const totalCount = boostStatistics.count; - return totalCount - loadedCount; + const tabs = useMemo(() => { + if (shouldDisplayGiftList) { + return [ + { type: 'boostList', title: lang('BoostingBoostsCount', boostStatistics?.boosts?.count) }, + { type: 'giftedBoostList', title: lang('BoostingGiftsCount', boostStatistics?.giftedBoosts?.count) }, + ]; + } + return []; + }, [shouldDisplayGiftList, boostStatistics?.boosts?.count, boostStatistics?.giftedBoosts?.count, lang]); + + const initialTab = useMemo(() => { + return boostStatistics?.boosts && boostStatistics.boosts?.list.length > 0 ? 1 : 0; }, [boostStatistics]); - const handleBoosterClick = useLastCallback((userId: string) => { + const [activeTab, setActiveTab] = useState(initialTab); + + const renderingActiveTab = activeTab > tabs.length - 1 ? tabs.length - 1 : activeTab; + + const tabType = tabs[renderingActiveTab]?.type; + + const activeKey = tabs.findIndex(({ type }) => type === tabType); + + const boostersToLoadCount = useMemo(() => { + if (!boostStatistics) return undefined; + + const list = shouldDisplayGiftList ? (tabType === 'boostList' + ? boostStatistics.boosts : boostStatistics.giftedBoosts) : boostStatistics.boosts; + if (!list?.count) return undefined; + + const loadedBoostsCount = list.list.reduce((total, boost) => { + return total + (boost.multiplier || 1); + }, 0); + + const totalCount = list.count; + const toLoadCount = totalCount - loadedBoostsCount; + + return toLoadCount > 0 ? toLoadCount : undefined; + }, [shouldDisplayGiftList, boostStatistics, tabType]); + + const renderBoostIcon = useLastCallback((multiplier: number) => ( +
+
+ +
{multiplier}
+
+
+ )); + + const renderBoostTypeIcon = useLastCallback((boost) => { + if (!boost.isFromGiveaway && !boost.isGift) { + return undefined; + } + + return ( +
+
+ +
{lang(boost.giveaway + ? 'lng_prizes_results_link' : 'BoostingGift')} +
+
+
+ ); + }); + + const handleBoosterClick = useLastCallback((userId?: string) => { + if (!userId) { + showNotification({ + message: lang('BoostingRecipientWillBeSelected'), + }); + return; + } openChat({ id: userId }); closeBoostStatistics(); }); + const renderBoostList = useLastCallback((boost) => { + return ( + handleBoosterClick(boost.userId)} + > + + + ); + }); + const handleGiveawayClick = useLastCallback(() => { openGiveawayModal({ chatId }); }); const handleLoadMore = useLastCallback(() => { - loadMoreBoosters(); + loadMoreBoosters({ isGifts: tabType === 'giftedBoostList' }); }); const launchPrepaidGiveawayHandler = useLastCallback((prepaidGiveaway: ApiPrepaidGiveaway) => { openGiveawayModal({ chatId, prepaidGiveaway }); }); + function renderContent() { + let listToRender; + if (tabType === 'boostList') { + listToRender = boostStatistics?.boosts?.list; + } else if (tabType === 'giftedBoostList') { + listToRender = boostStatistics?.giftedBoosts?.list; + } + + if (listToRender && !listToRender?.length) { + return undefined; + } + + return ( +
+ {listToRender?.map((boost) => renderBoostList(boost))} +
+ ); + } + return (
{!isLoaded && } @@ -152,7 +279,10 @@ const BoostStatistics = ({

{lang('PrepaidGiveawayMonths', prepaidGiveaway.months)}

-
+
{prepaidGiveaway.quantity * (giveawayBoostsPerPremium ?? GIVEAWAY_BOOST_PER_PREMIUM)} @@ -165,46 +295,52 @@ const BoostStatistics = ({

{lang('BoostingSelectPaidGiveaway')}

)} - {isChannel && ( -
-

- {lang('Boosters')} -

- {!boostStatistics.boosterIds?.length && ( -
{lang('NoBoostersHint')}
- )} - {boostStatistics.boosterIds?.map((userId) => ( - handleBoosterClick(userId)} +
+ {shouldDisplayGiftList ? ( +
+ - - - ))} - {Boolean(boostersToLoadCount) && ( - - {boostStatistics?.isLoadingBoosters ? ( - - ) : ( - - )} - {lang('ShowVotes', boostersToLoadCount)} - - )} -
- )} + {renderContent()} + + +
+ ) : ( + <> +

+ {lang('BoostingBoostsCount', boostStatistics?.boosts?.count)} +

+ {!boostStatistics?.boosts?.list?.length && ( +
{lang(isChannel ? 'NoBoostersHint' : 'NoBoostersGroupHint')} +
+ )} + {boostStatistics?.boosts?.list?.map((boost) => renderBoostList(boost))} + + )} + {Boolean(boostersToLoadCount) && ( + + {boostStatistics?.isLoadingBoosters ? ( + + ) : ( + + )} + {lang('ShowVotes', boostersToLoadCount)} + + )} +
{isGiveawayAvailable && (
diff --git a/src/components/ui/TabList.scss b/src/components/ui/TabList.scss index 2749de446..852e29b3c 100644 --- a/src/components/ui/TabList.scss +++ b/src/components/ui/TabList.scss @@ -17,7 +17,6 @@ &.big { font-size: 1rem; - --border-radius-messages-small: 0; } &::-webkit-scrollbar { diff --git a/src/global/actions/api/payments.ts b/src/global/actions/api/payments.ts index 4c71d7dcd..155db88ac 100644 --- a/src/global/actions/api/payments.ts +++ b/src/global/actions/api/payments.ts @@ -5,7 +5,7 @@ import { PaymentStep } from '../../../types'; import { DEBUG_PAYMENT_SMART_GLOCAL } from '../../../config'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; -import { buildCollectionByKey, unique } from '../../../util/iteratees'; +import { buildCollectionByKey } from '../../../util/iteratees'; import * as langProvider from '../../../util/langProvider'; import { getStripeError } from '../../../util/payments/stripe'; import { buildQueryString } from '../../../util/requestQuery'; @@ -578,13 +578,15 @@ addActionHandler('openBoostStatistics', async (global, actions, payload): Promis }, tabId); setGlobal(global); - const [boostsListResult, boostStatusResult] = await Promise.all([ + const [boostListResult, boostListGiftResult, + boostStatusResult] = await Promise.all([ callApi('fetchBoostList', { chat }), + callApi('fetchBoostList', { chat, isGifts: true }), callApi('fetchBoostStatus', { chat }), ]); global = getGlobal(); - if (!boostsListResult || !boostStatusResult) { + if (!boostListResult || !boostListGiftResult || !boostStatusResult) { global = updateTabState(global, { boostStatistics: undefined, }, tabId); @@ -592,22 +594,28 @@ addActionHandler('openBoostStatistics', async (global, actions, payload): Promis return; } - global = addUsers(global, buildCollectionByKey(boostsListResult.users, 'id')); + const totalBoostUserList = [...boostListResult.users, ...boostListGiftResult.users]; + global = addUsers(global, buildCollectionByKey(totalBoostUserList, 'id')); global = updateTabState(global, { boostStatistics: { chatId, boostStatus: boostStatusResult, - boosters: boostsListResult.boosters, - boosterIds: boostsListResult.boosterIds, - count: boostsListResult.count, - nextOffset: boostsListResult.nextOffset, + nextOffset: boostListResult.nextOffset, + boosts: { + count: boostListResult.count, + list: boostListResult.boostList, + }, + giftedBoosts: { + count: boostListGiftResult?.count, + list: boostListGiftResult?.boostList, + }, }, }, tabId); setGlobal(global); }); addActionHandler('loadMoreBoosters', async (global, actions, payload): Promise => { - const { tabId = getCurrentTabId() } = payload || {}; + const { isGifts, tabId = getCurrentTabId() } = payload || {}; let tabState = selectTabState(global, tabId); if (!tabState.boostStatistics) return; @@ -625,6 +633,7 @@ addActionHandler('loadMoreBoosters', async (global, actions, payload): Promise; - boosterIds?: string[]; boostStatus?: ApiBoostsStatus; isLoadingBoosters?: boolean; nextOffset?: string; - count?: number; + boosts?: { + count: number; + list: ApiBoost[]; + }; + giftedBoosts?: { + count: number; + list: ApiBoost[]; + }; }; giftCodeModal?: { @@ -2356,7 +2362,7 @@ export interface ActionPayloads { chatId: string; } & WithTabId; closeBoostStatistics: WithTabId | undefined; - loadMoreBoosters: WithTabId | undefined; + loadMoreBoosters: { isGifts?: boolean } & WithTabId | undefined; applyBoost: { slots: number[]; chatId: string; diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 14d690522..2823c7674 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -30,6 +30,8 @@ $color-error: #e53935; $color-warning: #fb8c00; $color-yellow: #fdd764; +$color-orange: #d08a31; +$color-light-coral: #d08a3133; $color-white: #ffffff; $color-black: #000000; @@ -115,6 +117,9 @@ $color-message-story-mention-to: #74bcff; --color-yellow: #{$color-yellow}; + --color-orange: #{$color-orange}; + --color-light-coral: #{$color-light-coral}; + --color-links: #{$color-links}; --color-own-links: #{$color-white};