Story: Block reply for non-premium users if messaging is restricted (#4732)
This commit is contained in:
parent
ed0c0c8325
commit
890cee4366
@ -343,6 +343,9 @@
|
||||
&.with-story-tweaks {
|
||||
border-radius: var(--border-radius-default-small);
|
||||
border-bottom-right-radius: 0;
|
||||
&.is-need-premium {
|
||||
border-bottom-right-radius: var(--border-radius-default-small);
|
||||
}
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@ -754,9 +757,12 @@
|
||||
.input-scroller {
|
||||
min-height: var(--base-height, 3.5rem);
|
||||
max-height: 10rem;
|
||||
|
||||
margin-right: calc((var(--margin-for-scrollbar) + 1rem) * -1);
|
||||
|
||||
&.is-need-premium {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
&:has(.form-control:focus) {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
@ -764,17 +770,52 @@
|
||||
|
||||
.input-scroller-content {
|
||||
margin-right: calc(var(--margin-for-scrollbar) + 0.5rem);
|
||||
&.is-need-premium {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
top: auto;
|
||||
bottom: 1.0625rem;
|
||||
left: 0.875rem;
|
||||
}
|
||||
|
||||
.unlock-button {
|
||||
&:hover,
|
||||
&.active {
|
||||
background: var(--color-chat-hover);
|
||||
}
|
||||
|
||||
color: var(--color-text);
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
padding-top: 0.125rem;
|
||||
padding-bottom: 0.125rem;
|
||||
padding-right: 0.4375rem;
|
||||
padding-left: 0.4375rem;
|
||||
|
||||
margin-left: 0.4375rem;
|
||||
|
||||
text-transform: lowercase;
|
||||
}
|
||||
}
|
||||
|
||||
#caption-input-text .placeholder-text {
|
||||
bottom: 1.0625rem;
|
||||
}
|
||||
|
||||
#story-input-text .placeholder-text {
|
||||
bottom: 0.875rem;
|
||||
top: calc((var(--base-height, 3.5rem) - var(--composer-text-size, 1rem) * 1.3125) / 2);
|
||||
|
||||
@media (max-width: 600px) {
|
||||
top: calc((2.875rem - var(--composer-text-size, 1rem) * 1.3125) / 2);
|
||||
}
|
||||
|
||||
&.is-need-premium {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
left: 0.875rem;
|
||||
}
|
||||
|
||||
|
||||
@ -257,6 +257,7 @@ type StateProps =
|
||||
canSendQuickReplies?: boolean;
|
||||
webPagePreview?: ApiWebPage;
|
||||
noWebPage?: boolean;
|
||||
isContactRequirePremium?: boolean;
|
||||
effect?: ApiAvailableEffect;
|
||||
effectReactions?: ApiReaction[];
|
||||
areEffectsSupported?: boolean;
|
||||
@ -370,6 +371,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onForward,
|
||||
webPagePreview,
|
||||
noWebPage,
|
||||
isContactRequirePremium,
|
||||
effect,
|
||||
effectReactions,
|
||||
areEffectsSupported,
|
||||
@ -490,8 +492,11 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
[chat, chatFullInfo, isChatWithBot, isInStoryViewer],
|
||||
);
|
||||
|
||||
const isNeedPremium = isContactRequirePremium && isInStoryViewer;
|
||||
const isSendTextBlocked = isNeedPremium || !canSendPlainText;
|
||||
|
||||
const hasWebPagePreview = !hasAttachments && canAttachEmbedLinks && !noWebPage && Boolean(webPagePreview);
|
||||
const isComposerBlocked = !canSendPlainText && !editingMessage;
|
||||
const isComposerBlocked = isSendTextBlocked && !editingMessage;
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasWebPagePreview) {
|
||||
@ -1601,7 +1606,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
editingMessage={editingMessage}
|
||||
/>
|
||||
)}
|
||||
{shouldRenderReactionSelector && (
|
||||
{shouldRenderReactionSelector && !isNeedPremium && (
|
||||
<ReactionSelector
|
||||
topReactions={topReactions}
|
||||
allAvailableReactions={availableReactions}
|
||||
@ -1684,30 +1689,35 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onClick={handleBotCommandSelect}
|
||||
onClose={closeChatCommandTooltip}
|
||||
/>
|
||||
<div className={buildClassName('composer-wrapper', isInStoryViewer && 'with-story-tweaks')}>
|
||||
<svg className="svg-appendix" width="9" height="20">
|
||||
<defs>
|
||||
<filter
|
||||
x="-50%"
|
||||
y="-14.7%"
|
||||
width="200%"
|
||||
height="141.2%"
|
||||
filterUnits="objectBoundingBox"
|
||||
id="composerAppendix"
|
||||
>
|
||||
<feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feColorMatrix
|
||||
values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0"
|
||||
in="shadowBlurOuter1"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#000" filter="url(#composerAppendix)" />
|
||||
<path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#FFF" className="corner" />
|
||||
</g>
|
||||
</svg>
|
||||
<div className={
|
||||
buildClassName('composer-wrapper', isInStoryViewer && 'with-story-tweaks', isNeedPremium && 'is-need-premium')
|
||||
}
|
||||
>
|
||||
{!isNeedPremium && (
|
||||
<svg className="svg-appendix" width="9" height="20">
|
||||
<defs>
|
||||
<filter
|
||||
x="-50%"
|
||||
y="-14.7%"
|
||||
width="200%"
|
||||
height="141.2%"
|
||||
filterUnits="objectBoundingBox"
|
||||
id="composerAppendix"
|
||||
>
|
||||
<feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feColorMatrix
|
||||
values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0"
|
||||
in="shadowBlurOuter1"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#000" filter="url(#composerAppendix)" />
|
||||
<path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#FFF" className="corner" />
|
||||
</g>
|
||||
</svg>
|
||||
)}
|
||||
{isInMessageList && (
|
||||
<>
|
||||
<InlineBotTooltip
|
||||
@ -1782,7 +1792,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(!isComposerBlocked || canSendGifs || canSendStickers) && (
|
||||
{((!isComposerBlocked || canSendGifs || canSendStickers) && !isNeedPremium) && (
|
||||
<SymbolMenuButton
|
||||
chatId={chatId}
|
||||
threadId={threadId}
|
||||
@ -1825,7 +1835,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
? ''
|
||||
: (!isComposerBlocked
|
||||
? (botKeyboardPlaceholder || inputPlaceholder || lang(placeholderForForumAsMessages || 'Message'))
|
||||
: lang('Chat.PlaceholderTextNotAllowed'))
|
||||
: isInStoryViewer ? lang('StoryRepliesLocked') : lang('Chat.PlaceholderTextNotAllowed'))
|
||||
}
|
||||
timedPlaceholderDate={timedPlaceholderDate}
|
||||
timedPlaceholderLangKey={timedPlaceholderLangKey}
|
||||
@ -1839,6 +1849,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onSuppressedFocus={closeSymbolMenu}
|
||||
onFocus={markInputHasFocus}
|
||||
onBlur={unmarkInputHasFocus}
|
||||
isNeedPremium={isNeedPremium}
|
||||
/>
|
||||
{isInMessageList && (
|
||||
<>
|
||||
@ -1875,28 +1886,30 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
{formatVoiceRecordDuration(currentRecordTime - startRecordTimeRef.current!)}
|
||||
</span>
|
||||
)}
|
||||
<AttachMenu
|
||||
chatId={chatId}
|
||||
threadId={threadId}
|
||||
editingMessage={editingMessage}
|
||||
hasReplaceableMedia={canMediaBeReplaced}
|
||||
isButtonVisible={!activeVoiceRecording}
|
||||
canAttachMedia={canAttachMedia}
|
||||
canAttachPolls={canAttachPolls}
|
||||
canSendPhotos={canSendPhotos}
|
||||
canSendVideos={canSendVideos}
|
||||
canSendDocuments={canSendDocuments}
|
||||
canSendAudios={canSendAudios}
|
||||
onFileSelect={handleFileSelect}
|
||||
onPollCreate={openPollModal}
|
||||
isScheduled={isInScheduledList}
|
||||
attachBots={isInMessageList ? attachBots : undefined}
|
||||
peerType={attachMenuPeerType}
|
||||
shouldCollectDebugLogs={shouldCollectDebugLogs}
|
||||
theme={theme}
|
||||
onMenuOpen={onAttachMenuOpen}
|
||||
onMenuClose={onAttachMenuClose}
|
||||
/>
|
||||
{!isNeedPremium && (
|
||||
<AttachMenu
|
||||
chatId={chatId}
|
||||
threadId={threadId}
|
||||
editingMessage={editingMessage}
|
||||
hasReplaceableMedia={canMediaBeReplaced}
|
||||
isButtonVisible={!activeVoiceRecording}
|
||||
canAttachMedia={canAttachMedia}
|
||||
canAttachPolls={canAttachPolls}
|
||||
canSendPhotos={canSendPhotos}
|
||||
canSendVideos={canSendVideos}
|
||||
canSendDocuments={canSendDocuments}
|
||||
canSendAudios={canSendAudios}
|
||||
onFileSelect={handleFileSelect}
|
||||
onPollCreate={openPollModal}
|
||||
isScheduled={isInScheduledList}
|
||||
attachBots={isInMessageList ? attachBots : undefined}
|
||||
peerType={attachMenuPeerType}
|
||||
shouldCollectDebugLogs={shouldCollectDebugLogs}
|
||||
theme={theme}
|
||||
onMenuOpen={onAttachMenuOpen}
|
||||
onMenuClose={onAttachMenuClose}
|
||||
/>
|
||||
)}
|
||||
{isInMessageList && Boolean(botKeyboardMessageId) && (
|
||||
<BotKeyboardMenu
|
||||
messageId={botKeyboardMessageId}
|
||||
@ -2126,6 +2139,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const noWebPage = selectNoWebPage(global, chatId, threadId);
|
||||
|
||||
const isContactRequirePremium = selectUserFullInfo(global, chatId)?.isContactRequirePremium;
|
||||
const areEffectsSupported = isChatWithUser && !isChatWithBot
|
||||
&& !isInScheduledList && !isChatWithSelf && type !== 'story' && chatId !== SERVICE_NOTIFICATIONS_USER_ID;
|
||||
const canPlayEffect = selectPerformanceSettingsValue(global, 'stickerEffects');
|
||||
@ -2203,6 +2217,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
canSendQuickReplies,
|
||||
noWebPage,
|
||||
webPagePreview: selectTabState(global).webPagePreview,
|
||||
isContactRequirePremium,
|
||||
effect,
|
||||
effectReactions,
|
||||
areEffectsSupported,
|
||||
|
||||
@ -33,6 +33,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
import useInputCustomEmojis from './hooks/useInputCustomEmojis';
|
||||
|
||||
import Button from '../../ui/Button';
|
||||
import TextTimer from '../../ui/TextTimer';
|
||||
import TextFormatter from './TextFormatter.async';
|
||||
|
||||
@ -72,6 +73,7 @@ type OwnProps = {
|
||||
captionLimit?: number;
|
||||
onFocus?: NoneToVoidFunction;
|
||||
onBlur?: NoneToVoidFunction;
|
||||
isNeedPremium?: boolean;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
@ -137,11 +139,13 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
onScroll,
|
||||
onFocus,
|
||||
onBlur,
|
||||
isNeedPremium,
|
||||
}) => {
|
||||
const {
|
||||
editLastMessage,
|
||||
replyToNextMessage,
|
||||
showAllowedMessageTypesNotification,
|
||||
openPremiumModal,
|
||||
} = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -257,7 +261,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
const chatIdRef = useRef(chatId);
|
||||
chatIdRef.current = chatId;
|
||||
const focusInput = useLastCallback(() => {
|
||||
if (!inputRef.current) {
|
||||
if (!inputRef.current || isNeedPremium) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -450,10 +454,12 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
if (isAttachmentModalInput || canSendPlainText) return;
|
||||
if (isAttachmentModalInput || canSendPlainText || (isStoryInput && isNeedPremium)) return;
|
||||
showAllowedMessageTypesNotification({ chatId });
|
||||
}
|
||||
|
||||
const handleOpenPremiumModal = useLastCallback(() => openPremiumModal());
|
||||
|
||||
useEffect(() => {
|
||||
if (IS_TOUCH_ENV) {
|
||||
return;
|
||||
@ -553,14 +559,16 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
shouldSuppressFocus && 'focus-disabled',
|
||||
);
|
||||
|
||||
const inputScrollerContentClass = buildClassName('input-scroller-content', isNeedPremium && 'is-need-premium');
|
||||
|
||||
return (
|
||||
<div id={id} onClick={shouldSuppressFocus ? onSuppressedFocus : undefined} dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
<div
|
||||
className={buildClassName('custom-scroll', SCROLLER_CLASS)}
|
||||
className={buildClassName('custom-scroll', SCROLLER_CLASS, isNeedPremium && 'is-need-premium')}
|
||||
onScroll={onScroll}
|
||||
onClick={!isAttachmentModalInput && !canSendPlainText ? handleClick : undefined}
|
||||
>
|
||||
<div className="input-scroller-content">
|
||||
<div className={inputScrollerContentClass}>
|
||||
<div
|
||||
ref={inputRef}
|
||||
id={editableInputId || EDITABLE_INPUT_ID}
|
||||
@ -576,14 +584,15 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
onContextMenu={IS_ANDROID ? handleAndroidContextMenu : undefined}
|
||||
onTouchCancel={IS_ANDROID ? processSelectionWithTimeout : undefined}
|
||||
aria-label={placeholder}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onFocus={!isNeedPremium ? onFocus : undefined}
|
||||
onBlur={!isNeedPremium ? onBlur : undefined}
|
||||
/>
|
||||
{!forcedPlaceholder && (
|
||||
<span
|
||||
className={buildClassName(
|
||||
'placeholder-text',
|
||||
!isAttachmentModalInput && !canSendPlainText && 'with-icon',
|
||||
isNeedPremium && 'is-need-premium',
|
||||
)}
|
||||
dir="auto"
|
||||
>
|
||||
@ -592,6 +601,11 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
{shouldDisplayTimer ? (
|
||||
<TextTimer langKey={timedPlaceholderLangKey!} endsAt={timedPlaceholderDate!} onEnd={handleTimerEnd} />
|
||||
) : placeholder}
|
||||
{isStoryInput && isNeedPremium && (
|
||||
<Button className="unlock-button" size="tiny" color="adaptive" onClick={handleOpenPremiumModal}>
|
||||
{lang('StoryRepliesLockedButton')}
|
||||
</Button>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
<canvas ref={sharedCanvasRef} className="shared-canvas" />
|
||||
@ -599,8 +613,14 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
<div ref={absoluteContainerRef} className="absolute-video-container" />
|
||||
</div>
|
||||
</div>
|
||||
<div ref={scrollerCloneRef} className={buildClassName('custom-scroll', SCROLLER_CLASS, 'clone')}>
|
||||
<div className="input-scroller-content">
|
||||
<div
|
||||
ref={scrollerCloneRef}
|
||||
className={buildClassName('custom-scroll',
|
||||
SCROLLER_CLASS,
|
||||
'clone',
|
||||
isNeedPremium && 'is-need-premium')}
|
||||
>
|
||||
<div className={inputScrollerContentClass}>
|
||||
<div ref={cloneRef} className={buildClassName(className, 'clone')} dir="auto" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user