TelegramPWA/src/components/left/settings/PrivacyMessages.tsx
Alexander Zinchuk 60f3995e82 Settings: Redesign (#6922)
Co-authored-by: Alexander Zinchuk <alx.zinchuk@gmail.com>
2026-06-01 01:15:35 +02:00

250 lines
8.6 KiB
TypeScript

import {
memo, useCallback, useMemo, useState,
} from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { GlobalState } from '../../../global/types';
import { SettingsScreens } from '../../../types';
import {
DEFAULT_CHARGE_FOR_MESSAGES,
} from '../../../config';
import {
selectIsCurrentUserPremium,
selectNewNoncontactPeersRequirePremium,
selectNonContactPeersPaidStars,
} from '../../../global/selectors';
import useDebouncedCallback from '../../../hooks/useDebouncedCallback';
import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import PaidMessagePrice from '../../common/paidMessage/PaidMessagePrice';
import Island, { IslandDescription, IslandTitle } from '../../gili/layout/Island';
import ListItem from '../../ui/ListItem';
import RadioGroup from '../../ui/RadioGroup';
import PremiumStatusItem from './PremiumStatusItem';
import PrivacyLockedOption from './PrivacyLockedOption';
type OwnProps = {
isActive?: boolean;
onReset: VoidFunction;
};
type StateProps = {
shouldNewNonContactPeersRequirePremium?: boolean;
shouldChargeForMessages?: boolean;
canLimitNewMessagesWithoutPremium?: boolean;
canChargeForMessages?: boolean;
isCurrentUserPremium?: boolean;
nonContactPeersPaidStars: number;
noPaidReactionsForUsersCount: number;
privacy: GlobalState['settings']['privacy'];
};
function PrivacyMessages({
isActive,
canLimitNewMessagesWithoutPremium,
canChargeForMessages,
shouldNewNonContactPeersRequirePremium,
shouldChargeForMessages,
nonContactPeersPaidStars,
isCurrentUserPremium,
noPaidReactionsForUsersCount,
onReset,
privacy,
}: OwnProps & StateProps) {
const { updateGlobalPrivacySettings, openSettingsScreen, showNotification } = getActions();
const oldLang = useOldLang();
const lang = useLang();
const canChangeForContactsAndPremium = isCurrentUserPremium || canLimitNewMessagesWithoutPremium;
const canChangeChargeForMessages = isCurrentUserPremium && canChargeForMessages;
const [chargeForMessages, setChargeForMessages] = useState<number>(nonContactPeersPaidStars);
const [hasShownNotification, setHasShownNotification] = useState(false);
const selectedValue = useMemo(() => {
if (shouldChargeForMessages) return 'charge_for_messages';
if (shouldNewNonContactPeersRequirePremium) return 'contacts_and_premium';
return 'everybody';
}, [shouldChargeForMessages, shouldNewNonContactPeersRequirePremium]);
useEffectWithPrevDeps(([prevSelectedValue]) => {
if (
!hasShownNotification && prevSelectedValue !== undefined
&& selectedValue !== 'everybody'
&& selectedValue !== prevSelectedValue
) {
if (privacy.chatInvite?.visibility === 'everybody') {
showNotification({
message: lang('CheckPrivacyInviteText'),
action: {
action: 'openSettingsScreen',
payload: { screen: SettingsScreens.PrivacyGroupChats },
},
actionText: { key: 'Review' },
duration: 8000,
});
}
if (privacy.phoneCall?.visibility === 'everybody') {
showNotification({
message: lang('CheckPrivacyCallsText'),
action: {
action: 'openSettingsScreen',
payload: { screen: SettingsScreens.PrivacyPhoneCall },
},
actionText: { key: 'Review' },
duration: 8000,
});
}
setHasShownNotification(true);
}
}, [selectedValue, shouldChargeForMessages, privacy, lang, hasShownNotification]);
const options = useMemo(() => {
return [
{ value: 'everybody', label: oldLang('P2PEverybody') },
{
value: 'contacts_and_premium',
label: canChangeForContactsAndPremium ? (
oldLang('PrivacyMessagesContactsAndPremium')
) : (
<PrivacyLockedOption
label={oldLang('PrivacyMessagesContactsAndPremium')}
isChecked={selectedValue === 'contacts_and_premium'}
/>
),
hidden: !canChangeForContactsAndPremium,
isCanCheckedInDisabled: true,
},
{
value: 'charge_for_messages',
label: canChangeChargeForMessages ? (
lang('PrivacyChargeForMessages')
) : (
<PrivacyLockedOption
label={lang('PrivacyChargeForMessages')}
isChecked={selectedValue === 'charge_for_messages'}
/>
),
hidden: !canChangeChargeForMessages,
isCanCheckedInDisabled: true,
},
];
}, [oldLang, lang, canChangeForContactsAndPremium, canChangeChargeForMessages, selectedValue]);
const handleChange = useLastCallback((privacyValue: string) => {
updateGlobalPrivacySettings({
shouldNewNonContactPeersRequirePremium: privacyValue === 'contacts_and_premium',
// eslint-disable-next-line no-null/no-null
nonContactPeersPaidStars: privacyValue === 'charge_for_messages' ? chargeForMessages : null,
});
});
const updateGlobalPrivacySettingsWithDebounced = useDebouncedCallback((value: number) => {
updateGlobalPrivacySettings({
nonContactPeersPaidStars: value,
});
}, [updateGlobalPrivacySettings], 300, true);
const handleChargeForMessagesChange = useCallback((value: number) => {
setChargeForMessages(value);
updateGlobalPrivacySettingsWithDebounced(value);
}, [setChargeForMessages, updateGlobalPrivacySettingsWithDebounced]);
function renderSectionNoPaidMessagesForUsers() {
const itemSubtitle = !noPaidReactionsForUsersCount ? lang('SubtitlePrivacyAddUsers')
: oldLang('Users', noPaidReactionsForUsersCount, 'i');
return (
<>
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
{lang('RemoveFeeTitle')}
</IslandTitle>
<Island>
<ListItem
narrow
icon="delete-user"
onClick={() => {
openSettingsScreen({ screen: SettingsScreens.PrivacyNoPaidMessages });
}}
>
<div className="multiline-item full-size">
<span className="title">{lang('ExceptionTitlePrivacyChargeForMessages')}</span>
<span className="subtitle">
{itemSubtitle}
</span>
</div>
</ListItem>
</Island>
</>
);
}
useHistoryBack({
isActive,
onBack: onReset,
});
const privacyDescription = useMemo(() => {
if (shouldChargeForMessages) return lang('PrivacyDescriptionChargeForMessages');
return lang('PrivacyDescriptionMessagesContactsAndPremium');
}, [shouldChargeForMessages, lang]);
return (
<div className="settings-content custom-scroll">
<IslandTitle dir={lang.isRtl ? 'rtl' : undefined}>
{oldLang('PrivacyMessagesTitle')}
</IslandTitle>
<Island>
<RadioGroup
name="privacy-messages"
options={options}
onChange={handleChange}
selected={selectedValue}
/>
</Island>
<IslandDescription dir={lang.isRtl ? 'rtl' : undefined}>
{privacyDescription}
</IslandDescription>
{selectedValue === 'charge_for_messages' && (
<Island>
<PaidMessagePrice
canChangeChargeForMessages={canChangeChargeForMessages}
chargeForMessages={chargeForMessages}
onChange={handleChargeForMessagesChange}
/>
</Island>
)}
{canChangeChargeForMessages && selectedValue === 'charge_for_messages' && renderSectionNoPaidMessagesForUsers()}
{!isCurrentUserPremium && selectedValue !== 'charge_for_messages'
&& <PremiumStatusItem premiumSection="message_privacy" />}
</div>
);
}
export default memo(withGlobal<OwnProps>((global): Complete<StateProps> => {
const {
settings: {
privacy,
},
} = global;
const nonContactPeersPaidStars = selectNonContactPeersPaidStars(global);
const noPaidReactionsForUsersCount = global.settings.privacy.noPaidMessages?.allowUserIds.length || 0;
return {
shouldNewNonContactPeersRequirePremium: selectNewNoncontactPeersRequirePremium(global),
shouldChargeForMessages: Boolean(nonContactPeersPaidStars),
nonContactPeersPaidStars: nonContactPeersPaidStars || DEFAULT_CHARGE_FOR_MESSAGES,
isCurrentUserPremium: selectIsCurrentUserPremium(global),
canLimitNewMessagesWithoutPremium: global.appConfig.canLimitNewMessagesWithoutPremium,
canChargeForMessages: global.appConfig.starsPaidMessagesAvailable,
noPaidReactionsForUsersCount,
privacy,
};
})(PrivacyMessages));