diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index eb4151e45..4d8cff788 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -623,6 +623,7 @@ async function getFullChannelInfo( hasScheduled, stargiftsCount, stargiftsAvailable, + paidMessagesAvailable, } = result.fullChat; if (chatPhoto) { @@ -717,6 +718,7 @@ async function getFullChannelInfo( hasScheduledMessages: hasScheduled, starGiftCount: stargiftsCount, areStarGiftsAvailable: Boolean(stargiftsAvailable), + arePaidMessagesAvailable: paidMessagesAvailable, }, chats, userStatusesById: statusesById, @@ -2022,6 +2024,19 @@ export async function fetchChannelRecommendations({ chat }: { chat?: ApiChat }) }; } +export async function updatePaidMessagesPrice({ + chat, paidMessagesStars, +}: { + chat?: ApiChat; paidMessagesStars: number; +}) { + return invokeRequest(new GramJs.channels.UpdatePaidMessagesPrice({ + channel: chat && buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel, + sendPaidMessagesStars: BigInt(paidMessagesStars), + }), { + shouldReturnTrue: true, + }); +} + export async function fetchSponsoredPeer({ query }: { query: string }) { const result = await invokeRequest(new GramJs.contacts.GetSponsoredPeers({ q: query })); if (!result || result instanceof GramJs.contacts.SponsoredPeersEmpty) return undefined; diff --git a/src/api/types/chats.ts b/src/api/types/chats.ts index 37de636a3..f23d2ac64 100644 --- a/src/api/types/chats.ts +++ b/src/api/types/chats.ts @@ -142,6 +142,7 @@ export interface ApiChatFullInfo { hasScheduledMessages?: boolean; starGiftCount?: number; areStarGiftsAvailable?: boolean; + arePaidMessagesAvailable?: true; boostsApplied?: number; boostsToUnrestrict?: number; diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index 110abf7f9..bb1228929 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -1894,7 +1894,7 @@ "ExceptionTitlePrivacyChargeForMessages" = "Remove fee"; "ExceptionDescriptionPrivacyChargeForMessages" = "Add users or entire groups who won't be charged for sending messages to you."; "SectionTitleStarsForForMessages" = "Set your price per message"; -"SectionDescriptionStarsForForMessages" = "You will receive {percent}% of the selected fee (~{amount}) for each incoming message."; +"SectionDescriptionStarsForForMessages" = "You will receive {percent} of the selected fee (~{amount}) for each incoming message."; "SubtitlePrivacyAddUsers" = "Add Users"; "PrivacyPaidMessagesValue" = "Paid"; "FirstMessageInPaidMessagesChat" = "**{user}** charges {amount} for each message."; @@ -1931,6 +1931,9 @@ "DescriptionRestrictedMedia" = "Posting media content is not allowed in this group."; "DescriptionScheduledPaidMediaNotAllowed" = "Posting scheduled paid media content is not allowed"; "DescriptionScheduledPaidMessagesNotAllowed" = "Scheduled paid messages is not allowed"; +"GroupMessagesChargePrice" = "Charge Stars for Messages"; +"RightsChargeStarsAbout" = "If you turn this on, regular members of the group will have to pay Stars to send messages."; +"SetPriceGroupDescription" = "Your group will receive {percent} of the selected fee (~{amount}) for each incoming messages."; "UnlockButtonTitle" = "Unlock with Telegram Premium"; "FrozenAccountModalTitle" = "Your Account is Frozen"; "FrozenAccountViolationTitle" = "Violation of Terms"; diff --git a/src/components/common/paidMessage/PaidMessagePrice.tsx b/src/components/common/paidMessage/PaidMessagePrice.tsx new file mode 100644 index 000000000..2b836fc15 --- /dev/null +++ b/src/components/common/paidMessage/PaidMessagePrice.tsx @@ -0,0 +1,115 @@ +import React, { + memo, +} from '../../../lib/teact/teact'; +import { getActions, withGlobal } from '../../../global'; + +import { + DEFAULT_MAXIMUM_CHARGE_FOR_MESSAGES, + MINIMUM_CHARGE_FOR_MESSAGES, +} from '../../../config'; +import { formatCurrencyAsString } from '../../../util/formatCurrency'; +import { formatPercent } from '../../../util/textFormat'; + +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; + +import Button from '../../ui/Button'; +import Icon from '../icons/Icon'; +import PaidMessageSlider from './PaidMessageSlider'; + +type OwnProps = { + chargeForMessages: number; + canChangeChargeForMessages?: boolean; + isGroupChat?: boolean; + onChange: (value: number) => void; +}; + +type StateProps = { + starsUsdWithdrawRate: number; + starsPaidMessageAmountMax: number; + starsPaidMessageCommissionPermille: number; +}; + +function PaidMessagePrice({ + starsUsdWithdrawRate, + starsPaidMessageAmountMax, + starsPaidMessageCommissionPermille, + canChangeChargeForMessages, + isGroupChat, + chargeForMessages, + onChange, +}: OwnProps & StateProps) { + const { openPremiumModal } = getActions(); + + const lang = useLang(); + + const handleChargeForMessagesChange = useLastCallback((value: number) => { + onChange?.(value); + }); + + const handleUnlockWithPremium = useLastCallback(() => { + openPremiumModal({ initialSection: 'message_privacy' }); + }); + + return ( + <> +

+ {lang('SectionTitleStarsForForMessages')} +

+ + {!canChangeChargeForMessages && ( + + )} + {canChangeChargeForMessages && ( +

+ {lang(isGroupChat ? 'SetPriceGroupDescription' : 'SectionDescriptionStarsForForMessages', { + percent: formatPercent(starsPaidMessageCommissionPermille * 100), + amount: formatCurrencyAsString( + chargeForMessages * starsUsdWithdrawRate * starsPaidMessageCommissionPermille, + 'USD', + lang.code, + ), + }, { + withNodes: true, + })} +

+ )} + + ); +} + +export default memo(withGlobal( + (global): StateProps => { + const starsUsdWithdrawRateX1000 = global.appConfig?.starsUsdWithdrawRateX1000; + const starsUsdWithdrawRate = starsUsdWithdrawRateX1000 ? starsUsdWithdrawRateX1000 / 1000 : 1; + const configStarsPaidMessageCommissionPermille = global.appConfig?.starsPaidMessageCommissionPermille; + const starsPaidMessageCommissionPermille = configStarsPaidMessageCommissionPermille + ? configStarsPaidMessageCommissionPermille / 1000 : 100; + + return { + starsPaidMessageCommissionPermille, + starsUsdWithdrawRate, + starsPaidMessageAmountMax: global.appConfig?.starsPaidMessageAmountMax || DEFAULT_MAXIMUM_CHARGE_FOR_MESSAGES, + }; + }, +)(PaidMessagePrice)); diff --git a/src/components/common/paidMessage/PaidMessageSlider.tsx b/src/components/common/paidMessage/PaidMessageSlider.tsx new file mode 100644 index 000000000..7f74096ac --- /dev/null +++ b/src/components/common/paidMessage/PaidMessageSlider.tsx @@ -0,0 +1,121 @@ +import type { FC } from '../../../lib/teact/teact'; +import React, { memo, useMemo } from '../../../lib/teact/teact'; + +import buildClassName from '../../../util/buildClassName'; +import { formatStarsAsText } from '../../../util/localization/format'; + +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; + +import Icon from '../icons/Icon'; + +type OwnProps = { + min?: number; + max: number; + value: number; + disabled?: boolean; + readOnly?: boolean; + bold?: boolean; + className?: string; + defaultValue: number; + onChange: (value: number) => void; + canChangeChargeForMessages?: boolean; +}; + +const DEFAULT_POINTS = [50, 100, 500, 1000, 2000, 5000, 10000]; + +const PaidMessageSlider: FC = ({ + min = 0, + max, + value, + disabled, + readOnly, + bold, + className, + defaultValue, + onChange, + canChangeChargeForMessages, +}) => { + const lang = useLang(); + + const points = useMemo(() => { + const result = []; + for (let i = 0; i < DEFAULT_POINTS.length; i++) { + if (DEFAULT_POINTS[i] < max) { + result.push(DEFAULT_POINTS[i]); + } + + if (DEFAULT_POINTS[i] >= max) { + result.push(max); + break; + } + } + + return result; + }, [max]); + + const handleChange = useLastCallback((event: React.ChangeEvent) => { + const newValue = Number(event.currentTarget.value); + onChange(getValue(points, newValue)); + }); + + const mainClassName = buildClassName( + className, + 'RangeSlider', + disabled && 'disabled', + readOnly && 'readOnly', + bold && 'bold', + ); + + function renderTopRow() { + return ( +
+ {lang.number(min)} + + {!canChangeChargeForMessages && ()} + {formatStarsAsText(lang, getValue(points, getProgress(points, value)))} + + {lang.number(max)} +
+ ); + } + + return ( +
+ {renderTopRow()} +
+
+ +
+
+ ); +}; + +function getProgress(points: number[], value: number) { + const pointIndex = points.findIndex((point) => value <= point); + const prevPoint = points[pointIndex - 1] || 1; + const nextPoint = points[pointIndex] || points[points.length - 1]; + const progress = (value - prevPoint) / (nextPoint - prevPoint); + return pointIndex + progress; +} + +function getValue(points: number[], progress: number) { + const pointIndex = Math.floor(progress); + const prevPoint = points[pointIndex - 1] || 1; + const nextPoint = points[pointIndex] || points[points.length - 1]; + const pointValue = prevPoint + (nextPoint - prevPoint) * (progress - pointIndex); + return pointValue < 100 ? Math.round(pointValue) : Math.round(pointValue / 10) * 10; +} + +export default memo(PaidMessageSlider); diff --git a/src/components/left/settings/PrivacyMessages.tsx b/src/components/left/settings/PrivacyMessages.tsx index bc90e7b06..450a36939 100644 --- a/src/components/left/settings/PrivacyMessages.tsx +++ b/src/components/left/settings/PrivacyMessages.tsx @@ -7,16 +7,12 @@ import { SettingsScreens } from '../../../types'; import { DEFAULT_CHARGE_FOR_MESSAGES, - DEFAULT_MAXIMUM_CHARGE_FOR_MESSAGES, - MINIMUM_CHARGE_FOR_MESSAGES, } from '../../../config'; import { selectIsCurrentUserPremium, selectNewNoncontactPeersRequirePremium, selectNonContactPeersPaidStars, } from '../../../global/selectors'; -import { formatCurrencyAsString } from '../../../util/formatCurrency'; -import { formatStarsAsText } from '../../../util/localization/format'; import useDebouncedCallback from '../../../hooks/useDebouncedCallback'; import useHistoryBack from '../../../hooks/useHistoryBack'; @@ -24,11 +20,9 @@ import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; -import Icon from '../../common/icons/Icon'; -import Button from '../../ui/Button'; +import PaidMessagePrice from '../../common/paidMessage/PaidMessagePrice'; import ListItem from '../../ui/ListItem'; import RadioGroup from '../../ui/RadioGroup'; -import RangeSlider from '../../ui/RangeSlider'; import PremiumStatusItem from './PremiumStatusItem'; import PrivacyLockedOption from './PrivacyLockedOption'; @@ -44,9 +38,6 @@ type StateProps = { canLimitNewMessagesWithoutPremium?: boolean; canChargeForMessages?: boolean; isCurrentUserPremium?: boolean; - starsUsdWithdrawRate: number; - starsPaidMessageCommissionPermille: number; - starsPaidMessageAmountMax?: number; nonContactPeersPaidStars: number; noPaidReactionsForUsersCount: number; }; @@ -59,14 +50,11 @@ function PrivacyMessages({ shouldChargeForMessages, nonContactPeersPaidStars, isCurrentUserPremium, - starsPaidMessageCommissionPermille, - starsPaidMessageAmountMax, - starsUsdWithdrawRate, noPaidReactionsForUsersCount, onReset, onScreenSelect, }: OwnProps & StateProps) { - const { updateGlobalPrivacySettings, openPremiumModal } = getActions(); + const { updateGlobalPrivacySettings } = getActions(); const oldLang = useOldLang(); const lang = useLang(); @@ -131,68 +119,6 @@ function PrivacyMessages({ updateGlobalPrivacySettingsWithDebounced(value); }, [setChargeForMessages, updateGlobalPrivacySettingsWithDebounced]); - const renderValueForStarsRange = useCallback((value: number) => { - return ( - - {!canChangeChargeForMessages && ()} - {formatStarsAsText(lang, value)} - - ); - }, [lang, canChangeChargeForMessages]); - - const handleUnlockWithPremium = useLastCallback(() => { - openPremiumModal({ initialSection: 'message_privacy' }); - }); - - function renderSectionStarsAmountForPaidMessages() { - return ( -
-

- {lang('SectionTitleStarsForForMessages')} -

- - {!isCurrentUserPremium && ( - - )} - {isCurrentUserPremium && ( -

- {lang('SectionDescriptionStarsForForMessages', { - percent: starsPaidMessageCommissionPermille * 100, - amount: formatCurrencyAsString( - chargeForMessages * starsUsdWithdrawRate * starsPaidMessageCommissionPermille, - 'USD', - lang.code, - ), - }, { - withNodes: true, - })} -

- )} -
- ); - } - function renderSectionNoPaidMessagesForUsers() { const itemSubtitle = !noPaidReactionsForUsersCount ? lang('SubtitlePrivacyAddUsers') : oldLang('Users', noPaidReactionsForUsersCount, 'i'); @@ -248,7 +174,15 @@ function PrivacyMessages({ {privacyDescription}

- {selectedValue === 'charge_for_messages' && renderSectionStarsAmountForPaidMessages()} + {selectedValue === 'charge_for_messages' && ( +
+ +
+ )} {canChangeChargeForMessages && selectedValue === 'charge_for_messages' && renderSectionNoPaidMessagesForUsers()} {!isCurrentUserPremium && selectedValue !== 'charge_for_messages' && } @@ -259,12 +193,6 @@ function PrivacyMessages({ export default memo(withGlobal((global): StateProps => { const nonContactPeersPaidStars = selectNonContactPeersPaidStars(global); - const starsUsdWithdrawRateX1000 = global.appConfig?.starsUsdWithdrawRateX1000; - const starsUsdWithdrawRate = starsUsdWithdrawRateX1000 ? starsUsdWithdrawRateX1000 / 1000 : 1; - const configStarsPaidMessageCommissionPermille = global.appConfig?.starsPaidMessageCommissionPermille; - const starsPaidMessageCommissionPermille = configStarsPaidMessageCommissionPermille - ? configStarsPaidMessageCommissionPermille / 1000 : 100; - const noPaidReactionsForUsersCount = global.settings.privacy.noPaidMessages?.allowUserIds.length || 0; return { @@ -274,9 +202,6 @@ export default memo(withGlobal((global): StateProps => { isCurrentUserPremium: selectIsCurrentUserPremium(global), canLimitNewMessagesWithoutPremium: global.appConfig?.canLimitNewMessagesWithoutPremium, canChargeForMessages: global.appConfig?.starsPaidMessagesAvailable, - starsPaidMessageAmountMax: global.appConfig?.starsPaidMessageAmountMax || DEFAULT_MAXIMUM_CHARGE_FOR_MESSAGES, - starsPaidMessageCommissionPermille, - starsUsdWithdrawRate, noPaidReactionsForUsersCount, }; })(PrivacyMessages)); diff --git a/src/components/left/settings/Settings.scss b/src/components/left/settings/Settings.scss index 89f2ef4bf..ff09b8033 100644 --- a/src/components/left/settings/Settings.scss +++ b/src/components/left/settings/Settings.scss @@ -125,6 +125,7 @@ color: var(--color-primary); display: inline-flex; align-items: center; + margin-inline-start: 2rem; } .settings-item-simple, @@ -283,11 +284,6 @@ } } - .RangeSlider { - margin-bottom: 1.0625rem; - padding-inline: 1rem; - } - .radio-group { .Radio:last-child { margin-bottom: 0; diff --git a/src/components/middle/message/ActionMessageText.tsx b/src/components/middle/message/ActionMessageText.tsx index af06dcd24..5b36ed149 100644 --- a/src/components/middle/message/ActionMessageText.tsx +++ b/src/components/middle/message/ActionMessageText.tsx @@ -3,7 +3,11 @@ import { getActions, getGlobal, withGlobal } from '../../../global'; import type { ApiChat, ApiMessage, ApiPeer } from '../../../api/types'; -import { GENERAL_TOPIC_ID, SERVICE_NOTIFICATIONS_USER_ID, TME_LINK_PREFIX } from '../../../config'; +import { + GENERAL_TOPIC_ID, + SERVICE_NOTIFICATIONS_USER_ID, + TME_LINK_PREFIX, +} from '../../../config'; import { getMessageInvoice, getMessageText, isChatChannel, } from '../../../global/helpers'; diff --git a/src/components/middle/panes/AudioPlayer.scss b/src/components/middle/panes/AudioPlayer.scss index 2e5fb461e..4ecc00a4b 100644 --- a/src/components/middle/panes/AudioPlayer.scss +++ b/src/components/middle/panes/AudioPlayer.scss @@ -96,7 +96,7 @@ box-shadow: 0 1px 2px var(--color-default-shadow); .RangeSlider { - margin-bottom: 0; + margin: 0; input[type="range"] { margin-bottom: 0; } diff --git a/src/components/right/management/ManageGroupPermissions.tsx b/src/components/right/management/ManageGroupPermissions.tsx index 348646518..7f1fae045 100644 --- a/src/components/right/management/ManageGroupPermissions.tsx +++ b/src/components/right/management/ManageGroupPermissions.tsx @@ -1,25 +1,38 @@ import type { FC } from '../../../lib/teact/teact'; import React, { - memo, useCallback, useMemo, useState, + memo, useCallback, useEffect, + useMemo, useState, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiChat, ApiChatBannedRights, ApiChatMember } from '../../../api/types'; -import { ManagementScreens } from '../../../types'; +import { ManagementProgress, ManagementScreens } from '../../../types'; -import { selectChat, selectChatFullInfo } from '../../../global/selectors'; +import { + DEFAULT_CHARGE_FOR_MESSAGES, +} from '../../../config'; +import { + selectChat, + selectChatFullInfo, + selectTabState, +} from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; +import useFlag from '../../../hooks/useFlag'; import useHistoryBack from '../../../hooks/useHistoryBack'; +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; import useManagePermissions from '../hooks/useManagePermissions'; import Icon from '../../common/icons/Icon'; +import PaidMessagePrice from '../../common/paidMessage/PaidMessagePrice'; import PrivateChatInfo from '../../common/PrivateChatInfo'; import PermissionCheckboxList from '../../main/PermissionCheckboxList'; import FloatingActionButton from '../../ui/FloatingActionButton'; import ListItem from '../../ui/ListItem'; import Spinner from '../../ui/Spinner'; +import Switcher from '../../ui/Switcher'; type OwnProps = { chatId: string; @@ -31,9 +44,13 @@ type OwnProps = { type StateProps = { chat?: ApiChat; + progress?: ManagementProgress; currentUserId?: string; removedUsersCount: number; members?: ApiChatMember[]; + arePaidMessagesAvailable?: boolean; + groupPeersPaidStars: number; + canChargeForMessages?: boolean; }; const ITEM_HEIGHT = 48; @@ -83,18 +100,23 @@ const ManageGroupPermissions: FC = ({ onScreenSelect, onChatMemberSelect, chat, + progress, currentUserId, removedUsersCount, members, onClose, isActive, + arePaidMessagesAvailable, + canChargeForMessages, + groupPeersPaidStars, }) => { - const { updateChatDefaultBannedRights } = getActions(); + const { updateChatDefaultBannedRights, updatePaidMessagesPrice } = getActions(); const { permissions, havePermissionChanged, isLoading, handlePermissionChange, setIsLoading, } = useManagePermissions(chat?.defaultBannedRights); - const lang = useOldLang(); + const oldLang = useOldLang(); + const lang = useLang(); useHistoryBack({ isActive, @@ -116,14 +138,41 @@ const ManageGroupPermissions: FC = ({ const [isMediaDropdownOpen, setIsMediaDropdownOpen] = useState(false); - const handleSavePermissions = useCallback(() => { + const [isPriceForMessagesChanged, markPriceForMessagesChanged, unmarkPriceForMessagesChanged] = useFlag(); + const [isPriceForMessagesOpen, setIsPriceForMessagesOpen] = useState(canChargeForMessages); + const [chargeForMessages, setChargeForMessages] = useState(groupPeersPaidStars); + + useEffect(() => { + if (progress === ManagementProgress.Complete) { + unmarkPriceForMessagesChanged(); + } + }, [progress]); + + const handleSavePermissions = useLastCallback(() => { if (!chat) { return; } setIsLoading(true); updateChatDefaultBannedRights({ chatId: chat.id, bannedRights: permissions }); - }, [chat, permissions, setIsLoading, updateChatDefaultBannedRights]); + }); + + const handleUpdatePaidMessagesPrice = useLastCallback(() => { + if (!chat) return; + updatePaidMessagesPrice({ + chatId: chat?.id, + paidMessagesStars: isPriceForMessagesOpen ? chargeForMessages : 0, + }); + }); + + const handleUpdatePermissions = useLastCallback(() => { + if (isPriceForMessagesChanged) { + handleUpdatePaidMessagesPrice(); + } + if (havePermissionChanged) { + handleSavePermissions(); + } + }); const exceptionMembers = useMemo(() => { if (!members) { @@ -157,11 +206,24 @@ const ManageGroupPermissions: FC = ({ return result; } - const translatedString = lang(langKey); + const translatedString = oldLang(langKey); return `${result}${!result.length ? translatedString : `, ${translatedString}`}`; }, ''); - }, [chat, lang]); + }, [chat, oldLang]); + + const handleChargeStarsForMessages = useLastCallback(() => { + setIsPriceForMessagesOpen(!isPriceForMessagesOpen); + markPriceForMessagesChanged(); + }); + + const handleChargeForMessagesChange = useLastCallback((value: number) => { + setChargeForMessages(value); + markPriceForMessagesChanged(); + }); + + const arePermissionsChanged = isPriceForMessagesChanged || havePermissionChanged; + const arePermissionsLoading = progress === ManagementProgress.InProgress || isLoading; return (
= ({ />
+ {arePaidMessagesAvailable && ( +
+ + {lang('GroupMessagesChargePrice')} + + +

+ {lang('RightsChargeStarsAbout')} +

+
+ )} + + {isPriceForMessagesOpen && ( +
+ +
+ )} +
= ({
- {isLoading ? ( + {arePermissionsLoading ? ( ) : ( @@ -256,12 +355,20 @@ export default memo(withGlobal( (global, { chatId }): StateProps => { const chat = selectChat(global, chatId); const fullInfo = selectChatFullInfo(global, chatId); + const { progress } = selectTabState(global).management; + + const paidMessagesStars = chat?.paidMessagesStars; + const configStarsPaidMessageCommissionPermille = global.appConfig?.starsPaidMessageCommissionPermille; return { chat, + progress, currentUserId: global.currentUserId, removedUsersCount: fullInfo?.kickedMembers?.length || 0, members: fullInfo?.members, + arePaidMessagesAvailable: Boolean(fullInfo?.arePaidMessagesAvailable && configStarsPaidMessageCommissionPermille), + canChargeForMessages: Boolean(paidMessagesStars && configStarsPaidMessageCommissionPermille), + groupPeersPaidStars: paidMessagesStars || DEFAULT_CHARGE_FOR_MESSAGES, }; }, )(ManageGroupPermissions)); diff --git a/src/components/right/management/Management.scss b/src/components/right/management/Management.scss index d173a7431..9ca855879 100644 --- a/src/components/right/management/Management.scss +++ b/src/components/right/management/Management.scss @@ -126,9 +126,7 @@ } .RangeSlider { - margin-top: 2rem; - margin-inline-start: 1rem; - margin-inline-end: 1rem; + margin-top: 1rem; } .button-position { diff --git a/src/components/ui/RangeSlider.scss b/src/components/ui/RangeSlider.scss index aa114c0c4..6088ec0a6 100644 --- a/src/components/ui/RangeSlider.scss +++ b/src/components/ui/RangeSlider.scss @@ -18,7 +18,9 @@ .RangeSlider { --slider-color: var(--color-primary); - margin-bottom: 1rem; + margin: 0.5rem 0 0; + margin-inline-start: 1rem; + margin-inline-end: 1rem; &.disabled { pointer-events: none; diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index f1a2bc288..49295fb6b 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -2896,6 +2896,27 @@ addActionHandler('toggleChannelRecommendations', (global, actions, payload): Act setGlobal(global); }); +addActionHandler('updatePaidMessagesPrice', async (global, actions, payload): Promise => { + const { chatId, paidMessagesStars, tabId = getCurrentTabId() } = payload; + const chat = chatId ? selectChat(global, chatId) : undefined; + if (!chat) return; + + global = updateManagementProgress(global, ManagementProgress.InProgress, tabId); + setGlobal(global); + + const result = await callApi('updatePaidMessagesPrice', { + chat, + paidMessagesStars, + }); + + if (!result) return; + + global = getGlobal(); + global = updateManagementProgress(global, ManagementProgress.Complete, tabId); + global = updateChat(global, chatId, { paidMessagesStars }); + setGlobal(global); +}); + addActionHandler('resolveBusinessChatLink', async (global, actions, payload): Promise => { const { slug, tabId = getCurrentTabId() } = payload; const result = await callApi('resolveBusinessChatLink', { slug }); diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index f20715f20..a60471ed5 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -1058,6 +1058,10 @@ export interface ActionPayloads { chatId: string; isEnabled: boolean; }; + updatePaidMessagesPrice: { + chatId: string; + paidMessagesStars: number; + } & WithTabId; updateChat: { chatId: string; diff --git a/src/lib/gramjs/tl/apiTl.ts b/src/lib/gramjs/tl/apiTl.ts index 052a8f1c0..1c3a6a41d 100644 --- a/src/lib/gramjs/tl/apiTl.ts +++ b/src/lib/gramjs/tl/apiTl.ts @@ -1709,6 +1709,7 @@ channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = U channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates; channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats; channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; +channels.updatePaidMessagesPrice#fc84653f channel:InputChannel send_paid_messages_stars:long = Updates; bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool; bots.canSendMessage#1359f4e6 bot:InputUser = Bool; bots.allowSendMessage#f132e3ef bot:InputUser = Updates; diff --git a/src/lib/gramjs/tl/static/api.json b/src/lib/gramjs/tl/static/api.json index 69021c00a..1095184d1 100644 --- a/src/lib/gramjs/tl/static/api.json +++ b/src/lib/gramjs/tl/static/api.json @@ -277,6 +277,7 @@ "channels.getChannelRecommendations", "channels.searchPosts", "channels.reportSpam", + "channels.updatePaidMessagesPrice", "bots.getBotRecommendations", "bots.canSendMessage", "bots.allowSendMessage", diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 077aa826e..c663bd034 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1475,6 +1475,8 @@ export interface LangPair { 'DescriptionRestrictedMedia': undefined; 'DescriptionScheduledPaidMediaNotAllowed': undefined; 'DescriptionScheduledPaidMessagesNotAllowed': undefined; + 'GroupMessagesChargePrice': undefined; + 'RightsChargeStarsAbout': undefined; 'UnlockButtonTitle': undefined; 'PrivacySubscribeToTelegramPremium': undefined; 'PrivacyDisableLimitedEditionStarGifts': undefined; @@ -2387,6 +2389,10 @@ export interface LangPairWithVariables { 'PaidMessageTransactionDescription': { 'percent': V; }; + 'SetPriceGroupDescription': { + 'percent': V; + 'amount': V; + }; 'FrozenAccountAppealSubtitle': { 'botLink': V; 'date': V;