Settings / Privacy: Voice Messages (#1968)
This commit is contained in:
parent
1eb030b2d8
commit
21a989b165
@ -71,6 +71,8 @@ export function buildPrivacyKey(key: GramJs.TypePrivacyKey): ApiPrivacyKey | und
|
||||
return 'phoneP2P';
|
||||
case 'PrivacyKeyForwards':
|
||||
return 'forwards';
|
||||
case 'PrivacyKeyVoiceMessages':
|
||||
return 'voiceMessages';
|
||||
case 'PrivacyKeyChatInvite':
|
||||
return 'chatInvite';
|
||||
}
|
||||
|
||||
@ -452,6 +452,9 @@ export function buildInputPrivacyKey(privacyKey: ApiPrivacyKey) {
|
||||
|
||||
case 'phoneP2P':
|
||||
return new GramJs.InputPrivacyKeyPhoneP2P();
|
||||
|
||||
case 'voiceMessages':
|
||||
return new GramJs.InputPrivacyKeyVoiceMessages();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@ -36,13 +36,13 @@ export async function fetchFullUser({
|
||||
}) {
|
||||
const input = buildInputEntity(id, accessHash);
|
||||
if (!(input instanceof GramJs.InputUser)) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fullInfo = await invokeRequest(new GramJs.users.GetFullUser({ id: input }));
|
||||
|
||||
if (!fullInfo) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (fullInfo.fullUser.profilePhoto instanceof GramJs.Photo) {
|
||||
@ -66,6 +66,8 @@ export async function fetchFullUser({
|
||||
fullInfo: userWithFullInfo.fullInfo,
|
||||
},
|
||||
});
|
||||
|
||||
return userWithFullInfo;
|
||||
}
|
||||
|
||||
export async function fetchCommonChats(id: string, accessHash?: string, maxId?: string) {
|
||||
|
||||
@ -149,14 +149,18 @@ const DeleteChatModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
function renderMessage() {
|
||||
if (isChannel && chat.isCreator) {
|
||||
return <p>{renderText(lang('ChatList.DeleteAndLeaveGroupConfirmation', chatTitle), ['simple_markdown'])}</p>;
|
||||
return (
|
||||
<p>
|
||||
{renderText(lang('ChatList.DeleteAndLeaveGroupConfirmation', chatTitle), ['simple_markdown', 'emoji'])}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
if ((isChannel && !chat.isCreator) || isBasicGroup || isSuperGroup) {
|
||||
return <p>{renderText(lang('ChannelLeaveAlertWithName', chatTitle), ['simple_markdown'])}</p>;
|
||||
return <p>{renderText(lang('ChannelLeaveAlertWithName', chatTitle), ['simple_markdown', 'emoji'])}</p>;
|
||||
}
|
||||
|
||||
return <p>{renderText(lang('ChatList.DeleteChatConfirmation', contactName), ['simple_markdown'])}</p>;
|
||||
return <p>{renderText(lang('ChatList.DeleteChatConfirmation', contactName), ['simple_markdown', 'emoji'])}</p>;
|
||||
}
|
||||
|
||||
function renderActionText() {
|
||||
|
||||
@ -164,6 +164,7 @@ const LeftColumn: FC<StateProps> = ({
|
||||
case SettingsScreens.PrivacyPhoneP2P:
|
||||
case SettingsScreens.PrivacyForwarding:
|
||||
case SettingsScreens.PrivacyGroupChats:
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
case SettingsScreens.PrivacyBlockedUsers:
|
||||
case SettingsScreens.ActiveWebsites:
|
||||
case SettingsScreens.TwoFaDisabled:
|
||||
@ -220,6 +221,10 @@ const LeftColumn: FC<StateProps> = ({
|
||||
case SettingsScreens.PrivacyForwardingDeniedContacts:
|
||||
setSettingsScreen(SettingsScreens.PrivacyForwarding);
|
||||
return;
|
||||
case SettingsScreens.PrivacyVoiceMessagesAllowedContacts:
|
||||
case SettingsScreens.PrivacyVoiceMessagesDeniedContacts:
|
||||
setSettingsScreen(SettingsScreens.PrivacyVoiceMessages);
|
||||
return;
|
||||
case SettingsScreens.PrivacyGroupChatsAllowedContacts:
|
||||
case SettingsScreens.PrivacyGroupChatsDeniedContacts:
|
||||
setSettingsScreen(SettingsScreens.PrivacyGroupChats);
|
||||
|
||||
@ -58,6 +58,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.settings-icon-locked {
|
||||
align-self: center;
|
||||
margin-right: 0.25rem !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
#monkey {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
@ -183,6 +189,7 @@
|
||||
|
||||
.multiline-menu-item {
|
||||
white-space: initial;
|
||||
flex-grow: 1;
|
||||
|
||||
&.full-size {
|
||||
width: 100%;
|
||||
|
||||
@ -104,6 +104,11 @@ const PRIVACY_FORWARDING_SCREENS = [
|
||||
SettingsScreens.PrivacyForwardingDeniedContacts,
|
||||
];
|
||||
|
||||
const PRIVACY_VOICE_MESSAGES_SCREENS = [
|
||||
SettingsScreens.PrivacyVoiceMessagesAllowedContacts,
|
||||
SettingsScreens.PrivacyVoiceMessagesDeniedContacts,
|
||||
];
|
||||
|
||||
const PRIVACY_GROUP_CHATS_SCREENS = [
|
||||
SettingsScreens.PrivacyGroupChatsAllowedContacts,
|
||||
SettingsScreens.PrivacyGroupChatsDeniedContacts,
|
||||
@ -178,6 +183,7 @@ const Settings: FC<OwnProps> = ({
|
||||
[SettingsScreens.PrivacyPhoneCall]: PRIVACY_PHONE_CALL_SCREENS.includes(screen),
|
||||
[SettingsScreens.PrivacyPhoneP2P]: PRIVACY_PHONE_P2P_SCREENS.includes(screen),
|
||||
[SettingsScreens.PrivacyForwarding]: PRIVACY_FORWARDING_SCREENS.includes(screen),
|
||||
[SettingsScreens.PrivacyVoiceMessages]: PRIVACY_VOICE_MESSAGES_SCREENS.includes(screen),
|
||||
[SettingsScreens.PrivacyGroupChats]: PRIVACY_GROUP_CHATS_SCREENS.includes(screen),
|
||||
};
|
||||
|
||||
@ -284,6 +290,7 @@ const Settings: FC<OwnProps> = ({
|
||||
case SettingsScreens.PrivacyPhoneCall:
|
||||
case SettingsScreens.PrivacyPhoneP2P:
|
||||
case SettingsScreens.PrivacyForwarding:
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
case SettingsScreens.PrivacyGroupChats:
|
||||
return (
|
||||
<SettingsPrivacyVisibility
|
||||
@ -300,6 +307,7 @@ const Settings: FC<OwnProps> = ({
|
||||
case SettingsScreens.PrivacyPhoneCallAllowedContacts:
|
||||
case SettingsScreens.PrivacyPhoneP2PAllowedContacts:
|
||||
case SettingsScreens.PrivacyForwardingAllowedContacts:
|
||||
case SettingsScreens.PrivacyVoiceMessagesAllowedContacts:
|
||||
case SettingsScreens.PrivacyGroupChatsAllowedContacts:
|
||||
return (
|
||||
<SettingsPrivacyVisibilityExceptionList
|
||||
@ -317,6 +325,7 @@ const Settings: FC<OwnProps> = ({
|
||||
case SettingsScreens.PrivacyPhoneCallDeniedContacts:
|
||||
case SettingsScreens.PrivacyPhoneP2PDeniedContacts:
|
||||
case SettingsScreens.PrivacyForwardingDeniedContacts:
|
||||
case SettingsScreens.PrivacyVoiceMessagesDeniedContacts:
|
||||
case SettingsScreens.PrivacyGroupChatsDeniedContacts:
|
||||
return (
|
||||
<SettingsPrivacyVisibilityExceptionList
|
||||
|
||||
@ -110,18 +110,22 @@ const SettingsHeader: FC<OwnProps> = ({
|
||||
return <h3>{lang('Privacy.ProfilePhoto')}</h3>;
|
||||
case SettingsScreens.PrivacyForwarding:
|
||||
return <h3>{lang('PrivacyForwards')}</h3>;
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
return <h3>{lang('PrivacyVoiceMessages')}</h3>;
|
||||
case SettingsScreens.PrivacyGroupChats:
|
||||
return <h3>{lang('AutodownloadGroupChats')}</h3>;
|
||||
case SettingsScreens.PrivacyPhoneNumberAllowedContacts:
|
||||
case SettingsScreens.PrivacyLastSeenAllowedContacts:
|
||||
case SettingsScreens.PrivacyProfilePhotoAllowedContacts:
|
||||
case SettingsScreens.PrivacyForwardingAllowedContacts:
|
||||
case SettingsScreens.PrivacyVoiceMessagesAllowedContacts:
|
||||
case SettingsScreens.PrivacyGroupChatsAllowedContacts:
|
||||
return <h3>{lang('AlwaysShareWith')}</h3>;
|
||||
case SettingsScreens.PrivacyPhoneNumberDeniedContacts:
|
||||
case SettingsScreens.PrivacyLastSeenDeniedContacts:
|
||||
case SettingsScreens.PrivacyProfilePhotoDeniedContacts:
|
||||
case SettingsScreens.PrivacyForwardingDeniedContacts:
|
||||
case SettingsScreens.PrivacyVoiceMessagesDeniedContacts:
|
||||
case SettingsScreens.PrivacyGroupChatsDeniedContacts:
|
||||
return <h3>{lang('NeverShareWith')}</h3>;
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import type { ApiPrivacySettings } from '../../../types';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { selectIsCurrentUserPremium } from '../../../global/selectors';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
|
||||
@ -18,6 +20,7 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
isCurrentUserPremium?: boolean;
|
||||
hasPassword?: boolean;
|
||||
hasPasscode?: boolean;
|
||||
blockedCount: number;
|
||||
@ -29,6 +32,7 @@ type StateProps = {
|
||||
privacyLastSeen?: ApiPrivacySettings;
|
||||
privacyProfilePhoto?: ApiPrivacySettings;
|
||||
privacyForwarding?: ApiPrivacySettings;
|
||||
privacyVoiceMessages?: ApiPrivacySettings;
|
||||
privacyGroupChats?: ApiPrivacySettings;
|
||||
privacyPhoneCall?: ApiPrivacySettings;
|
||||
privacyPhoneP2P?: ApiPrivacySettings;
|
||||
@ -36,6 +40,7 @@ type StateProps = {
|
||||
|
||||
const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
isCurrentUserPremium,
|
||||
hasPassword,
|
||||
hasPasscode,
|
||||
blockedCount,
|
||||
@ -47,6 +52,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
privacyLastSeen,
|
||||
privacyProfilePhoto,
|
||||
privacyForwarding,
|
||||
privacyVoiceMessages,
|
||||
privacyGroupChats,
|
||||
privacyPhoneCall,
|
||||
privacyPhoneP2P,
|
||||
@ -62,6 +68,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
loadGlobalPrivacySettings,
|
||||
updateGlobalPrivacySettings,
|
||||
loadWebAuthorizations,
|
||||
showNotification,
|
||||
} = getActions();
|
||||
|
||||
useEffect(() => {
|
||||
@ -91,6 +98,16 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}, [updateGlobalPrivacySettings]);
|
||||
|
||||
const handleVoiceMessagesClick = useCallback(() => {
|
||||
if (isCurrentUserPremium) {
|
||||
onScreenSelect(SettingsScreens.PrivacyVoiceMessages);
|
||||
} else {
|
||||
showNotification({
|
||||
message: lang('PrivacyVoiceMessagesPremiumOnly'),
|
||||
});
|
||||
}
|
||||
}, [isCurrentUserPremium, lang, onScreenSelect, showNotification]);
|
||||
|
||||
function getVisibilityValue(setting?: ApiPrivacySettings) {
|
||||
const { visibility } = setting || {};
|
||||
const blockCount = setting ? setting.blockChatIds.length + setting.blockUserIds.length : 0;
|
||||
@ -256,6 +273,21 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
</span>
|
||||
</div>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
narrow
|
||||
disabled={!isCurrentUserPremium}
|
||||
allowDisabledClick
|
||||
rightElement={!isCurrentUserPremium && <i className="icon-lock-badge settings-icon-locked" />}
|
||||
className="no-icon"
|
||||
onClick={handleVoiceMessagesClick}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
<span className="title">{lang('PrivacyVoiceMessages')}</span>
|
||||
<span className="subtitle" dir="auto">
|
||||
{getVisibilityValue(privacyVoiceMessages)}
|
||||
</span>
|
||||
</div>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
narrow
|
||||
className="no-icon"
|
||||
@ -317,6 +349,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
} = global;
|
||||
|
||||
return {
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
hasPassword,
|
||||
hasPasscode: Boolean(hasPasscode),
|
||||
blockedCount: blocked.totalCount,
|
||||
@ -328,6 +361,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
privacyLastSeen: privacy.lastSeen,
|
||||
privacyProfilePhoto: privacy.profilePhoto,
|
||||
privacyForwarding: privacy.forwards,
|
||||
privacyVoiceMessages: privacy.voiceMessages,
|
||||
privacyGroupChats: privacy.chatInvite,
|
||||
privacyPhoneCall: privacy.phoneCall,
|
||||
privacyPhoneP2P: privacy.phoneP2P,
|
||||
|
||||
@ -77,6 +77,8 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps> = ({
|
||||
return lang('PrivacyProfilePhotoTitle');
|
||||
case SettingsScreens.PrivacyForwarding:
|
||||
return lang('PrivacyForwardsTitle');
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
return lang('PrivacyVoiceMessagesTitle');
|
||||
case SettingsScreens.PrivacyGroupChats:
|
||||
return lang('WhoCanAddMe');
|
||||
case SettingsScreens.PrivacyPhoneCall:
|
||||
@ -116,6 +118,8 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps> = ({
|
||||
return SettingsScreens.PrivacyPhoneCallAllowedContacts;
|
||||
case SettingsScreens.PrivacyPhoneP2P:
|
||||
return SettingsScreens.PrivacyPhoneP2PAllowedContacts;
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
return SettingsScreens.PrivacyVoiceMessagesAllowedContacts;
|
||||
default:
|
||||
return SettingsScreens.PrivacyGroupChatsAllowedContacts;
|
||||
}
|
||||
@ -135,6 +139,8 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps> = ({
|
||||
return SettingsScreens.PrivacyPhoneCallDeniedContacts;
|
||||
case SettingsScreens.PrivacyPhoneP2P:
|
||||
return SettingsScreens.PrivacyPhoneP2PDeniedContacts;
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
return SettingsScreens.PrivacyVoiceMessagesDeniedContacts;
|
||||
default:
|
||||
return SettingsScreens.PrivacyGroupChatsDeniedContacts;
|
||||
}
|
||||
@ -258,6 +264,10 @@ export default memo(withGlobal<OwnProps>(
|
||||
privacySettings = privacy.forwards;
|
||||
break;
|
||||
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
privacySettings = privacy.voiceMessages;
|
||||
break;
|
||||
|
||||
case SettingsScreens.PrivacyGroupChats:
|
||||
privacySettings = privacy.chatInvite;
|
||||
break;
|
||||
|
||||
@ -143,6 +143,9 @@ function getCurrentPrivacySettings(global: GlobalState, screen: SettingsScreens)
|
||||
case SettingsScreens.PrivacyForwardingAllowedContacts:
|
||||
case SettingsScreens.PrivacyForwardingDeniedContacts:
|
||||
return privacy.forwards;
|
||||
case SettingsScreens.PrivacyVoiceMessagesAllowedContacts:
|
||||
case SettingsScreens.PrivacyVoiceMessagesDeniedContacts:
|
||||
return privacy.voiceMessages;
|
||||
case SettingsScreens.PrivacyGroupChatsDeniedContacts:
|
||||
case SettingsScreens.PrivacyGroupChatsAllowedContacts:
|
||||
return privacy.chatInvite;
|
||||
|
||||
@ -19,6 +19,10 @@ export function getPrivacyKey(screen: SettingsScreens): ApiPrivacyKey | undefine
|
||||
case SettingsScreens.PrivacyForwardingAllowedContacts:
|
||||
case SettingsScreens.PrivacyForwardingDeniedContacts:
|
||||
return 'forwards';
|
||||
case SettingsScreens.PrivacyVoiceMessages:
|
||||
case SettingsScreens.PrivacyVoiceMessagesAllowedContacts:
|
||||
case SettingsScreens.PrivacyVoiceMessagesDeniedContacts:
|
||||
return 'voiceMessages';
|
||||
case SettingsScreens.PrivacyGroupChats:
|
||||
case SettingsScreens.PrivacyGroupChatsAllowedContacts:
|
||||
case SettingsScreens.PrivacyGroupChatsDeniedContacts:
|
||||
|
||||
@ -40,7 +40,7 @@ const BotTrustModal: FC<OwnProps> = ({ bot, type }) => {
|
||||
onClose={cancelBotTrustRequest}
|
||||
confirmHandler={handleBotTrustAccept}
|
||||
title={title}
|
||||
textParts={renderText(text, ['br', 'simple_markdown'])}
|
||||
textParts={renderText(text, ['br', 'simple_markdown', 'emoji'])}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -153,7 +153,8 @@ const Dialogs: FC<StateProps> = ({ dialogs }) => {
|
||||
className="error"
|
||||
title={getErrorHeader(error)}
|
||||
>
|
||||
{error.hasErrorKey ? getReadableErrorText(error) : renderText(error.message!, ['emoji', 'br'])}
|
||||
{error.hasErrorKey ? getReadableErrorText(error)
|
||||
: renderText(error.message!, ['simple_markdown', 'emoji', 'br'])}
|
||||
<div>
|
||||
<Button isText onClick={closeModal}>{lang('OK')}</Button>
|
||||
</div>
|
||||
|
||||
@ -26,11 +26,11 @@ const Notifications: FC<StateProps> = ({ notifications }) => {
|
||||
message, className, localId, action, actionText, title,
|
||||
}) => (
|
||||
<Notification
|
||||
title={title ? renderText(title, ['emoji', 'br', 'links', 'simple_markdown']) : undefined}
|
||||
title={title ? renderText(title, ['simple_markdown', 'emoji', 'br', 'links']) : undefined}
|
||||
action={action}
|
||||
actionText={actionText}
|
||||
className={className}
|
||||
message={renderText(message, ['emoji', 'br', 'links', 'simple_markdown'])}
|
||||
message={renderText(message, ['simple_markdown', 'emoji', 'br', 'links'])}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onDismiss={() => dismissNotification({ localId })}
|
||||
/>
|
||||
|
||||
@ -85,7 +85,6 @@ type StateProps = {
|
||||
audioMessage?: ApiMessage;
|
||||
messagesCount?: number;
|
||||
isChatWithSelf?: boolean;
|
||||
isChatWithBot?: boolean;
|
||||
lastSyncTime?: number;
|
||||
hasButtonInHeader?: boolean;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
@ -111,7 +110,6 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
chat,
|
||||
messagesCount,
|
||||
isChatWithSelf,
|
||||
isChatWithBot,
|
||||
lastSyncTime,
|
||||
hasButtonInHeader,
|
||||
shouldSkipHistoryAnimations,
|
||||
@ -338,7 +336,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
typingStatus={typingStatus}
|
||||
status={connectionStatusText}
|
||||
withDots={Boolean(connectionStatusText)}
|
||||
withFullInfo={isChatWithBot}
|
||||
withFullInfo
|
||||
withMediaViewer
|
||||
withUpdatingStatus
|
||||
withVideoAvatar
|
||||
@ -483,7 +481,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
chat,
|
||||
messagesCount,
|
||||
isChatWithSelf: selectIsChatWithSelf(global, chatId),
|
||||
isChatWithBot,
|
||||
lastSyncTime,
|
||||
shouldSkipHistoryAnimations,
|
||||
currentTransitionKey: Math.max(0, global.messages.messageLists.length - 1),
|
||||
|
||||
@ -175,6 +175,7 @@ type StateProps =
|
||||
fileSizeLimit: number;
|
||||
captionLimit: number;
|
||||
isCurrentUserPremium?: boolean;
|
||||
canSendVoiceByPrivacy?: boolean;
|
||||
}
|
||||
& Pick<GlobalState, 'connectionState'>;
|
||||
|
||||
@ -214,6 +215,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
chat,
|
||||
isForCurrentMessageList,
|
||||
isCurrentUserPremium,
|
||||
canSendVoiceByPrivacy,
|
||||
connectionState,
|
||||
isChatWithBot,
|
||||
isChatWithSelf,
|
||||
@ -269,6 +271,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
resetOpenChatWithText,
|
||||
callAttachMenuBot,
|
||||
openLimitReachedModal,
|
||||
showNotification,
|
||||
} = getActions();
|
||||
const lang = useLang();
|
||||
|
||||
@ -903,14 +906,26 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [isSelectModeActive, enableHover, disableHover, isReady]);
|
||||
|
||||
const areVoiceMessagesNotAllowed = mainButtonState === MainButtonState.Record
|
||||
&& (!canAttachMedia || !canSendVoiceByPrivacy);
|
||||
|
||||
const mainButtonHandler = useCallback(() => {
|
||||
switch (mainButtonState) {
|
||||
case MainButtonState.Send:
|
||||
handleSend();
|
||||
break;
|
||||
case MainButtonState.Record:
|
||||
void startRecordingVoice();
|
||||
case MainButtonState.Record: {
|
||||
if (areVoiceMessagesNotAllowed) {
|
||||
if (!canSendVoiceByPrivacy) {
|
||||
showNotification({
|
||||
message: lang('VoiceMessagesRestrictedByPrivacy', chat?.title),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
startRecordingVoice();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MainButtonState.Edit:
|
||||
handleEditComplete();
|
||||
break;
|
||||
@ -926,12 +941,11 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
break;
|
||||
}
|
||||
}, [
|
||||
mainButtonState, handleSend, startRecordingVoice, handleEditComplete, activeVoiceRecording, requestCalendar,
|
||||
pauseRecordingVoice, handleMessageSchedule,
|
||||
mainButtonState, handleSend, handleEditComplete, activeVoiceRecording, requestCalendar, areVoiceMessagesNotAllowed,
|
||||
canSendVoiceByPrivacy, showNotification, lang, chat?.title, startRecordingVoice, pauseRecordingVoice,
|
||||
handleMessageSchedule,
|
||||
]);
|
||||
|
||||
const areVoiceMessagesNotAllowed = mainButtonState === MainButtonState.Record && !canAttachMedia;
|
||||
|
||||
const prevEditedMessage = usePrevious(editingMessage, true);
|
||||
const renderedEditedMessage = editingMessage || prevEditedMessage;
|
||||
|
||||
@ -948,7 +962,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
sendButtonAriaLabel = 'Save edited message';
|
||||
break;
|
||||
case MainButtonState.Record:
|
||||
sendButtonAriaLabel = areVoiceMessagesNotAllowed
|
||||
sendButtonAriaLabel = !canAttachMedia
|
||||
? 'Conversation.DefaultRestrictedMedia'
|
||||
: 'AccDescrVoiceMessage';
|
||||
}
|
||||
@ -1254,6 +1268,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
color="secondary"
|
||||
className={buildClassName(mainButtonState, !isReady && 'not-ready', activeVoiceRecording && 'recording')}
|
||||
disabled={areVoiceMessagesNotAllowed}
|
||||
allowDisabledClick
|
||||
ariaLabel={lang(sendButtonAriaLabel)}
|
||||
onClick={mainButtonHandler}
|
||||
onContextMenu={
|
||||
@ -1305,6 +1320,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
const isForCurrentMessageList = chatId === currentMessageList?.chatId
|
||||
&& threadId === currentMessageList?.threadId
|
||||
&& messageListType === currentMessageList?.type;
|
||||
const user = selectUser(global, chatId);
|
||||
const canSendVoiceByPrivacy = (user && !user.fullInfo?.noVoiceMessages) ?? true;
|
||||
|
||||
const editingDraft = messageListType === 'scheduled'
|
||||
? selectEditingScheduledDraft(global, chatId)
|
||||
@ -1358,6 +1375,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
fileSizeLimit: selectCurrentLimit(global, 'uploadMaxFileparts') * MAX_UPLOAD_FILEPART_SIZE,
|
||||
captionLimit: selectCurrentLimit(global, 'captionLength'),
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
canSendVoiceByPrivacy,
|
||||
};
|
||||
},
|
||||
)(Composer));
|
||||
|
||||
@ -55,7 +55,9 @@
|
||||
&.disabled {
|
||||
opacity: 0.5 !important;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
&:not(.click-allowed) {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.round {
|
||||
|
||||
@ -33,6 +33,7 @@ export type OwnProps = {
|
||||
href?: string;
|
||||
download?: string;
|
||||
disabled?: boolean;
|
||||
allowDisabledClick?: boolean;
|
||||
ripple?: boolean;
|
||||
faded?: boolean;
|
||||
tabIndex?: number;
|
||||
@ -79,6 +80,7 @@ const Button: FC<OwnProps> = ({
|
||||
href,
|
||||
download,
|
||||
disabled,
|
||||
allowDisabledClick,
|
||||
ripple,
|
||||
faded,
|
||||
tabIndex,
|
||||
@ -104,6 +106,7 @@ const Button: FC<OwnProps> = ({
|
||||
pill && 'pill',
|
||||
fluid && 'fluid',
|
||||
disabled && 'disabled',
|
||||
allowDisabledClick && 'click-allowed',
|
||||
isText && 'text',
|
||||
isLoading && 'loading',
|
||||
ripple && 'has-ripple',
|
||||
@ -114,7 +117,7 @@ const Button: FC<OwnProps> = ({
|
||||
);
|
||||
|
||||
const handleClick = useCallback((e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
if (!disabled && onClick) {
|
||||
if ((allowDisabledClick || !disabled) && onClick) {
|
||||
onClick(e);
|
||||
}
|
||||
|
||||
@ -124,15 +127,15 @@ const Button: FC<OwnProps> = ({
|
||||
setTimeout(() => {
|
||||
setIsClicked(false);
|
||||
}, CLICKED_TIMEOUT);
|
||||
}, [disabled, onClick, shouldStopPropagation]);
|
||||
}, [allowDisabledClick, disabled, onClick, shouldStopPropagation]);
|
||||
|
||||
const handleMouseDown = useCallback((e: ReactMouseEvent<HTMLButtonElement>) => {
|
||||
if (!noPreventDefault) e.preventDefault();
|
||||
|
||||
if (!disabled && onMouseDown) {
|
||||
if ((allowDisabledClick || !disabled) && onMouseDown) {
|
||||
onMouseDown(e);
|
||||
}
|
||||
}, [disabled, noPreventDefault, onMouseDown]);
|
||||
}, [allowDisabledClick, disabled, noPreventDefault, onMouseDown]);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
|
||||
@ -69,12 +69,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
&.disabled:not(.click-allowed) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ListItem-button {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&.disabled .ListItem-button {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:not(.disabled):not(.is-static) {
|
||||
@ -86,6 +86,7 @@
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
--background-color: var(--color-chat-hover);
|
||||
@ -202,7 +203,8 @@
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.PremiumIcon, .VerifiedIcon {
|
||||
.PremiumIcon,
|
||||
.VerifiedIcon {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
|
||||
@ -30,11 +30,13 @@ interface OwnProps {
|
||||
icon?: string;
|
||||
leftElement?: TeactNode;
|
||||
secondaryIcon?: string;
|
||||
rightElement?: TeactNode;
|
||||
buttonClassName?: string;
|
||||
className?: string;
|
||||
style?: string;
|
||||
children: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
allowDisabledClick?: boolean;
|
||||
ripple?: boolean;
|
||||
narrow?: boolean;
|
||||
inactive?: boolean;
|
||||
@ -56,10 +58,12 @@ const ListItem: FC<OwnProps> = ({
|
||||
leftElement,
|
||||
buttonClassName,
|
||||
secondaryIcon,
|
||||
rightElement,
|
||||
className,
|
||||
style,
|
||||
children,
|
||||
disabled,
|
||||
allowDisabledClick,
|
||||
ripple,
|
||||
narrow,
|
||||
inactive,
|
||||
@ -108,7 +112,7 @@ const ListItem: FC<OwnProps> = ({
|
||||
);
|
||||
|
||||
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
if (disabled || !onClick) {
|
||||
if ((disabled && !allowDisabledClick) || !onClick) {
|
||||
return;
|
||||
}
|
||||
onClick(e);
|
||||
@ -117,10 +121,10 @@ const ListItem: FC<OwnProps> = ({
|
||||
markIsTouched();
|
||||
fastRaf(unmarkIsTouched);
|
||||
}
|
||||
}, [disabled, markIsTouched, onClick, ripple, unmarkIsTouched]);
|
||||
}, [allowDisabledClick, disabled, markIsTouched, onClick, ripple, unmarkIsTouched]);
|
||||
|
||||
const handleSecondaryIconClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
if (disabled || e.button !== 0 || (!onSecondaryIconClick && !contextActions)) return;
|
||||
if ((disabled && !allowDisabledClick) || e.button !== 0 || (!onSecondaryIconClick && !contextActions)) return;
|
||||
e.stopPropagation();
|
||||
if (onSecondaryIconClick) {
|
||||
onSecondaryIconClick(e);
|
||||
@ -154,6 +158,7 @@ const ListItem: FC<OwnProps> = ({
|
||||
ripple && 'has-ripple',
|
||||
narrow && 'narrow',
|
||||
disabled && 'disabled',
|
||||
allowDisabledClick && 'click-allowed',
|
||||
inactive && 'inactive',
|
||||
contextMenuPosition && 'has-menu-open',
|
||||
focus && 'focus',
|
||||
@ -201,6 +206,7 @@ const ListItem: FC<OwnProps> = ({
|
||||
<i className={`icon-${secondaryIcon}`} />
|
||||
</Button>
|
||||
)}
|
||||
{rightElement}
|
||||
</div>
|
||||
{contextActions && contextMenuPosition !== undefined && (
|
||||
<Menu
|
||||
|
||||
@ -40,7 +40,6 @@ const Notification: FC<OwnProps> = ({
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const timerRef = useRef<number | undefined>(null);
|
||||
|
||||
const { transitionClassNames } = useShowTransition(isOpen);
|
||||
|
||||
const closeAndDismiss = useCallback(() => {
|
||||
|
||||
@ -69,9 +69,12 @@ import {
|
||||
selectUser,
|
||||
selectSendAs,
|
||||
selectSponsoredMessage,
|
||||
selectForwardsContainVoiceMessages,
|
||||
} from '../../selectors';
|
||||
import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers';
|
||||
import { getMessageOriginalId, isServiceNotificationMessage } from '../../helpers';
|
||||
import {
|
||||
debounce, onTickEnd, rafPromise,
|
||||
} from '../../../util/schedulers';
|
||||
import { getMessageOriginalId, getUserFullName, isServiceNotificationMessage } from '../../helpers';
|
||||
import { getTranslation } from '../../../util/langProvider';
|
||||
import { ensureProtocol } from '../../../util/ensureProtocol';
|
||||
|
||||
@ -1245,6 +1248,39 @@ addActionHandler('openUrl', (global, actions, payload) => {
|
||||
}
|
||||
});
|
||||
|
||||
addActionHandler('setForwardChatId', async (global, actions, payload) => {
|
||||
const { id } = payload;
|
||||
let user = selectUser(global, id);
|
||||
if (user && selectForwardsContainVoiceMessages(global)) {
|
||||
if (!user.fullInfo) {
|
||||
const { accessHash } = user;
|
||||
user = await callApi('fetchFullUser', { id, accessHash });
|
||||
}
|
||||
|
||||
if (user?.fullInfo!.noVoiceMessages) {
|
||||
actions.showDialog({
|
||||
data: {
|
||||
message: getTranslation('VoiceMessagesRestrictedByPrivacy', getUserFullName(user)),
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setGlobal({
|
||||
...global,
|
||||
forwardMessages: {
|
||||
...global.forwardMessages,
|
||||
toChatId: id,
|
||||
isModalShown: false,
|
||||
},
|
||||
});
|
||||
|
||||
actions.openChat({ id });
|
||||
actions.closeMediaViewer();
|
||||
actions.exitMessageSelectMode();
|
||||
});
|
||||
|
||||
function countSortedIds(ids: number[], from: number, to: number) {
|
||||
let count = 0;
|
||||
|
||||
|
||||
@ -318,6 +318,7 @@ addActionHandler('loadPrivacySettings', async (global) => {
|
||||
chatInviteSettings,
|
||||
phoneCallSettings,
|
||||
phoneP2PSettings,
|
||||
voiceMessagesSettings,
|
||||
] = await Promise.all([
|
||||
callApi('fetchPrivacySettings', 'phoneNumber'),
|
||||
callApi('fetchPrivacySettings', 'lastSeen'),
|
||||
@ -326,6 +327,7 @@ addActionHandler('loadPrivacySettings', async (global) => {
|
||||
callApi('fetchPrivacySettings', 'chatInvite'),
|
||||
callApi('fetchPrivacySettings', 'phoneCall'),
|
||||
callApi('fetchPrivacySettings', 'phoneP2P'),
|
||||
callApi('fetchPrivacySettings', 'voiceMessages'),
|
||||
]);
|
||||
|
||||
if (
|
||||
@ -336,6 +338,7 @@ addActionHandler('loadPrivacySettings', async (global) => {
|
||||
|| !chatInviteSettings
|
||||
|| !phoneCallSettings
|
||||
|| !phoneP2PSettings
|
||||
|| !voiceMessagesSettings
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -354,6 +357,7 @@ addActionHandler('loadPrivacySettings', async (global) => {
|
||||
chatInvite: chatInviteSettings,
|
||||
phoneCall: phoneCallSettings,
|
||||
phoneP2P: phoneP2PSettings,
|
||||
voiceMessages: voiceMessagesSettings,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -471,23 +471,6 @@ addActionHandler('exitForwardMode', (global) => {
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('setForwardChatId', (global, actions, payload) => {
|
||||
const { id } = payload;
|
||||
|
||||
setGlobal({
|
||||
...global,
|
||||
forwardMessages: {
|
||||
...global.forwardMessages,
|
||||
toChatId: id,
|
||||
isModalShown: false,
|
||||
},
|
||||
});
|
||||
|
||||
actions.openChat({ id });
|
||||
actions.closeMediaViewer();
|
||||
actions.exitMessageSelectMode();
|
||||
});
|
||||
|
||||
addActionHandler('openForwardMenuForSelectedMessages', (global, actions) => {
|
||||
if (!global.selectedMessages) {
|
||||
return;
|
||||
|
||||
@ -975,3 +975,13 @@ export function selectCanScheduleUntilOnline(global: GlobalState, id: string) {
|
||||
!isChatWithSelf && !chatBot && isUserId(id) && selectUserStatus(global, id)?.wasOnline,
|
||||
);
|
||||
}
|
||||
|
||||
export function selectForwardsContainVoiceMessages(global: GlobalState) {
|
||||
const { messageIds, fromChatId } = global.forwardMessages;
|
||||
if (!messageIds) return false;
|
||||
const chatMessages = selectChatMessages(global, fromChatId!);
|
||||
return messageIds.some((messageId) => {
|
||||
const message = chatMessages[messageId];
|
||||
return Boolean(message.content.voice) || message.content.video?.isRound;
|
||||
});
|
||||
}
|
||||
|
||||
@ -174,6 +174,7 @@ export enum SettingsScreens {
|
||||
PrivacyPhoneCall,
|
||||
PrivacyPhoneP2P,
|
||||
PrivacyForwarding,
|
||||
PrivacyVoiceMessages,
|
||||
PrivacyGroupChats,
|
||||
PrivacyPhoneNumberAllowedContacts,
|
||||
PrivacyPhoneNumberDeniedContacts,
|
||||
@ -187,6 +188,8 @@ export enum SettingsScreens {
|
||||
PrivacyPhoneP2PDeniedContacts,
|
||||
PrivacyForwardingAllowedContacts,
|
||||
PrivacyForwardingDeniedContacts,
|
||||
PrivacyVoiceMessagesAllowedContacts,
|
||||
PrivacyVoiceMessagesDeniedContacts,
|
||||
PrivacyGroupChatsAllowedContacts,
|
||||
PrivacyGroupChatsDeniedContacts,
|
||||
PrivacyBlockedUsers,
|
||||
@ -327,7 +330,7 @@ export enum NewChatMembersProgress {
|
||||
|
||||
export type ProfileTabType = 'members' | 'commonChats' | 'media' | 'documents' | 'links' | 'audio' | 'voice';
|
||||
export type SharedMediaType = 'media' | 'documents' | 'links' | 'audio' | 'voice';
|
||||
export type ApiPrivacyKey = 'phoneNumber' | 'lastSeen' | 'profilePhoto' |
|
||||
export type ApiPrivacyKey = 'phoneNumber' | 'lastSeen' | 'profilePhoto' | 'voiceMessages' |
|
||||
'forwards' | 'chatInvite' | 'phoneCall' | 'phoneP2P';
|
||||
export type PrivacyVisibility = 'everybody' | 'contacts' | 'nonContacts' | 'nobody';
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user