From 62f742c0237f6a160563f22c8c4585c002d75f94 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sun, 12 Nov 2023 12:54:49 +0400 Subject: [PATCH] Boost Modal: Support multiple boosts for one channel (#3963) --- src/api/gramjs/methods/payments.ts | 23 ++++++++++++++---- src/components/common/helpers/boostInfo.ts | 6 ++--- src/components/modals/boost/BoostModal.tsx | 27 +++++++++++++++------- src/global/actions/api/payments.ts | 16 ++++++++++++- 4 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/api/gramjs/methods/payments.ts b/src/api/gramjs/methods/payments.ts index da87e75a9..07c283f54 100644 --- a/src/api/gramjs/methods/payments.ts +++ b/src/api/gramjs/methods/payments.ts @@ -212,19 +212,32 @@ export async function fetchMyBoosts() { }; } -export function applyBoost({ +export async function applyBoost({ chat, slots, } : { chat: ApiChat; slots: number[]; }) { - return invokeRequest(new GramJs.premium.ApplyBoost({ + const result = await invokeRequest(new GramJs.premium.ApplyBoost({ peer: buildInputPeer(chat.id, chat.accessHash), slots, - }), { - shouldReturnTrue: true, - }); + })); + + if (!result) return undefined; + + addEntitiesToLocalDb(result.users); + addEntitiesToLocalDb(result.chats); + + const users = result.users.map(buildApiUser).filter(Boolean); + const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); + const boosts = result.myBoosts.map(buildApiMyBoost); + + return { + users, + chats, + boosts, + }; } export async function fetchBoostsStatus({ diff --git a/src/components/common/helpers/boostInfo.ts b/src/components/common/helpers/boostInfo.ts index 380b150c0..466078074 100644 --- a/src/components/common/helpers/boostInfo.ts +++ b/src/components/common/helpers/boostInfo.ts @@ -5,11 +5,11 @@ export function getBoostProgressInfo(boostInfo: ApiBoostsStatus, freezeOnLevelUp level, boosts, currentLevelBoosts, nextLevelBoosts, hasMyBoost, } = boostInfo; - const currentLevel = level; - const hasNextLevel = Boolean(nextLevelBoosts); - const isJustUpgraded = freezeOnLevelUp && boosts === currentLevelBoosts && hasMyBoost; + const currentLevel = isJustUpgraded ? level - 1 : level; + const hasNextLevel = Boolean(nextLevelBoosts); + const levelProgress = (!nextLevelBoosts || isJustUpgraded) ? 1 : (boosts - currentLevelBoosts) / (nextLevelBoosts - currentLevelBoosts); const remainingBoosts = nextLevelBoosts ? nextLevelBoosts - boosts : 0; diff --git a/src/components/modals/boost/BoostModal.tsx b/src/components/modals/boost/BoostModal.tsx index 915615f48..1bd898828 100644 --- a/src/components/modals/boost/BoostModal.tsx +++ b/src/components/modals/boost/BoostModal.tsx @@ -33,6 +33,7 @@ type LoadedParams = { progress: number; descriptionText: string; isBoosted?: boolean; + canBoostMore?: boolean; }; type BoostInfo = ({ @@ -100,6 +101,7 @@ const BoostModal = ({ value, progress, descriptionText, + canBoostMore, }: BoostInfo = useMemo(() => { if (!info?.boostStatus || !chat) { return { @@ -112,7 +114,8 @@ const BoostModal = ({ level, currentLevelBoosts, hasMyBoost, } = info.boostStatus; - const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts); + const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts, chat.id); + const areBoostsInDifferentChannels = info?.myBoosts && !areAllBoostsInChannel(info.myBoosts, chat.id); const { boosts, @@ -163,10 +166,11 @@ const BoostModal = ({ descriptionText: description, boost: firstBoost, isBoosted: hasBoost, + canBoostMore: areBoostsInDifferentChannels, }; }, [chat, chatTitle, info, lang]); - const isBoostDisabled = !boost && isCurrentUserPremium; + const isBoostDisabled = !info?.myBoosts?.length && isCurrentUserPremium; const isReplacingBoost = boost?.chatId && boost.chatId !== info?.chatId; const handleApplyBoost = useLastCallback(() => { @@ -187,10 +191,11 @@ const BoostModal = ({ openPremiumDialog(); } + closeBoostModal(); return; } - if (isBoosted) { + if (!canBoostMore) { closeBoostModal(); return; } @@ -231,10 +236,10 @@ const BoostModal = ({
@@ -316,14 +321,20 @@ const BoostModal = ({ ); }; -function getFirstAvailableBoost(myBoosts: ApiMyBoost[]) { - return myBoosts.find((boost) => !boost.chatId) || myBoosts.sort((a, b) => a.date - b.date)[0]; +function getFirstAvailableBoost(myBoosts: ApiMyBoost[], chatId?: string) { + return myBoosts.find((boost) => !boost.chatId) + || myBoosts.filter((b) => chatId && b.chatId !== chatId) + .sort((a, b) => a.date - b.date)[0]; +} + +function areAllBoostsInChannel(myBoosts: ApiMyBoost[], chatId: string) { + return myBoosts.every((boost) => boost.chatId === chatId); } export default memo(withGlobal( (global, { info }): StateProps => { const chat = info && selectChat(global, info?.chatId); - const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts); + const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts, info.chatId); const boostedChat = firstBoost?.chatId ? selectChat(global, firstBoost?.chatId) : undefined; return { diff --git a/src/global/actions/api/payments.ts b/src/global/actions/api/payments.ts index 204661eef..94dffe97f 100644 --- a/src/global/actions/api/payments.ts +++ b/src/global/actions/api/payments.ts @@ -611,6 +611,20 @@ addActionHandler('applyBoost', async (global, actions, payload): Promise = return; } + global = getGlobal(); + let tabState = selectTabState(global, tabId); + global = addUsers(global, buildCollectionByKey(result.users, 'id')); + global = addChats(global, buildCollectionByKey(result.chats, 'id')); + if (tabState.boostModal) { + global = updateTabState(global, { + boostModal: { + ...tabState.boostModal, + myBoosts: result.boosts, + }, + }, tabId); + } + setGlobal(global); + const newStatusResult = await callApi('fetchBoostsStatus', { chat, }); @@ -620,7 +634,7 @@ addActionHandler('applyBoost', async (global, actions, payload): Promise = } global = getGlobal(); - const tabState = selectTabState(global, tabId); + tabState = selectTabState(global, tabId); if (!tabState.boostModal?.boostStatus) return; global = updateTabState(global, { boostModal: {