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 (
+
+ );
+};
+
+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;