Premium Modal: Display user emoji status (#3776)
This commit is contained in:
parent
a7700015ac
commit
44c31ff6a0
@ -44,6 +44,7 @@ type OwnProps = {
|
||||
noStatusOrTyping?: boolean;
|
||||
noRtl?: boolean;
|
||||
adminMember?: ApiChatMember;
|
||||
onEmojiStatusClick?: NoneToVoidFunction;
|
||||
};
|
||||
|
||||
type StateProps =
|
||||
@ -75,6 +76,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
areMessagesLoaded,
|
||||
adminMember,
|
||||
ripple,
|
||||
onEmojiStatusClick,
|
||||
}) => {
|
||||
const {
|
||||
loadFullUser,
|
||||
@ -159,6 +161,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
withEmojiStatus={!noEmojiStatus}
|
||||
emojiStatusSize={emojiStatusSize}
|
||||
isSavedMessages={isSavedMessages}
|
||||
onEmojiStatusClick={onEmojiStatusClick}
|
||||
/>
|
||||
{customTitle && <span className="custom-title">{customTitle}</span>}
|
||||
</div>
|
||||
@ -171,6 +174,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
withEmojiStatus={!noEmojiStatus}
|
||||
emojiStatusSize={emojiStatusSize}
|
||||
isSavedMessages={isSavedMessages}
|
||||
onEmojiStatusClick={onEmojiStatusClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
.custom-emoji {
|
||||
--custom-emoji-size: 1.5rem;
|
||||
|
||||
color: var(--color-white);
|
||||
color: var(--color-white) !important;
|
||||
pointer-events: auto;
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
}
|
||||
|
||||
@ -138,10 +138,10 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
});
|
||||
|
||||
const handleClickPremium = useLastCallback(() => {
|
||||
if (!user) return;
|
||||
const handleStatusClick = useLastCallback(() => {
|
||||
if (!userId) return;
|
||||
|
||||
openPremiumModal({ fromUserId: user.id });
|
||||
openPremiumModal({ fromUserId: userId });
|
||||
});
|
||||
|
||||
const selectPreviousMedia = useLastCallback(() => {
|
||||
@ -334,7 +334,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
withEmojiStatus
|
||||
emojiStatusSize={EMOJI_STATUS_SIZE}
|
||||
isSavedMessages={isSavedMessages}
|
||||
onEmojiStatusClick={handleClickPremium}
|
||||
onEmojiStatusClick={handleStatusClick}
|
||||
noLoopLimit
|
||||
canCopyTitle
|
||||
/>
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 0.5rem 1rem;
|
||||
padding: 0.5rem;
|
||||
|
||||
&.with-top-border {
|
||||
/* stylelint-disable-next-line plugin/whole-pixel */
|
||||
@ -24,13 +24,13 @@
|
||||
|
||||
text-align: center;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
&.custom-emoji {
|
||||
--emoji-size: 2.25rem;
|
||||
&.custom-emoji .modal-content {
|
||||
--emoji-size: 2.25rem;
|
||||
|
||||
.stickers {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
.stickers {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,4 +27,5 @@
|
||||
align-self: center;
|
||||
border-radius: 0.625rem;
|
||||
background: var(--item-color, #000);
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
@import '../../../styles/mixins';
|
||||
|
||||
.root {
|
||||
--premium-gradient: linear-gradient(88.39deg, #6C93FF -2.56%, #976FFF 51.27%, #DF69D1 107.39%);
|
||||
--premium-feature-background: linear-gradient(65.85deg, #6C93FF -0.24%, #976FFF 53.99%, #DF69D1 110.53%);
|
||||
@ -30,12 +32,14 @@
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 1rem;
|
||||
padding: 1rem 0.5rem;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
overflow: scroll;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
@include adapt-padding-to-scrollbar(0.5rem);
|
||||
}
|
||||
|
||||
.logo {
|
||||
@ -45,14 +49,23 @@
|
||||
min-height: 6.25rem;
|
||||
}
|
||||
|
||||
.status-emoji {
|
||||
--custom-emoji-size: 8rem;
|
||||
|
||||
margin: 1rem;
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
}
|
||||
|
||||
.header-text {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-inline: 0.5rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
text-align: center;
|
||||
margin-inline: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@ -119,6 +132,25 @@
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stickerSetText {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.stickerSetLink {
|
||||
--custom-emoji-size: 1.5rem;
|
||||
|
||||
color: var(--color-links);
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.stickerSetLinkIcon {
|
||||
vertical-align: middle;
|
||||
margin-inline-end: 0.25rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.root :global(.modal-dialog) {
|
||||
|
||||
@ -4,7 +4,9 @@ import React, {
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiPremiumPromo, ApiUser } from '../../../api/types';
|
||||
import type {
|
||||
ApiPremiumPromo, ApiSticker, ApiStickerSet, ApiUser,
|
||||
} from '../../../api/types';
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
|
||||
import PremiumFeatureModal, {
|
||||
@ -15,19 +17,24 @@ import PremiumFeatureModal, {
|
||||
import { TME_LINK_PREFIX } from '../../../config';
|
||||
import { formatCurrency } from '../../../util/formatCurrency';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { selectTabState, selectIsCurrentUserPremium, selectUser } from '../../../global/selectors';
|
||||
import {
|
||||
selectTabState, selectIsCurrentUserPremium, selectUser, selectStickerSet,
|
||||
} from '../../../global/selectors';
|
||||
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||
import { selectPremiumLimit } from '../../../global/selectors/limits';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import { getUserFullName } from '../../../global/helpers';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
import Modal from '../../ui/Modal';
|
||||
import Button from '../../ui/Button';
|
||||
import PremiumFeatureItem from './PremiumFeatureItem';
|
||||
import Transition from '../../ui/Transition';
|
||||
import CustomEmoji from '../../common/CustomEmoji';
|
||||
|
||||
import PremiumLogo from '../../../assets/premium/PremiumLogo.svg';
|
||||
import PremiumLimits from '../../../assets/premium/PremiumLimits.svg';
|
||||
@ -47,6 +54,7 @@ import PremiumTranslate from '../../../assets/premium/PremiumTranslate.svg';
|
||||
import styles from './PremiumMainModal.module.scss';
|
||||
|
||||
const LIMIT_ACCOUNTS = 4;
|
||||
const STATUS_EMOJI_SIZE = 8 * REM;
|
||||
|
||||
const PREMIUM_FEATURE_COLOR_ICONS: Record<string, string> = {
|
||||
double_limits: PremiumLimits,
|
||||
@ -73,6 +81,8 @@ type StateProps = {
|
||||
promo?: ApiPremiumPromo;
|
||||
isClosing?: boolean;
|
||||
fromUser?: ApiUser;
|
||||
fromUserStatusEmoji?: ApiSticker;
|
||||
fromUserStatusSet?: ApiStickerSet;
|
||||
toUser?: ApiUser;
|
||||
initialSection?: string;
|
||||
isPremium?: boolean;
|
||||
@ -93,6 +103,8 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
isOpen,
|
||||
currentUserId,
|
||||
fromUser,
|
||||
fromUserStatusEmoji,
|
||||
fromUserStatusSet,
|
||||
promo,
|
||||
initialSection,
|
||||
isPremium,
|
||||
@ -113,7 +125,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const dialogRef = useRef<HTMLDivElement>(null);
|
||||
const {
|
||||
closePremiumModal, openInvoice, requestConfetti, openTelegramLink,
|
||||
closePremiumModal, openInvoice, requestConfetti, openTelegramLink, loadStickers, openStickerSet,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
@ -148,9 +160,9 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
const handleClick = useLastCallback(() => {
|
||||
handleClickWithStartParam();
|
||||
}
|
||||
});
|
||||
|
||||
const showConfetti = useCallback(() => {
|
||||
const dialog = dialogRef.current;
|
||||
@ -185,7 +197,39 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
return premiumPromoOrder.filter((section) => PREMIUM_FEATURE_SECTIONS.includes(section));
|
||||
}, [premiumPromoOrder]);
|
||||
|
||||
if (!promo) return undefined;
|
||||
useEffect(() => {
|
||||
if (!fromUserStatusEmoji || fromUserStatusSet) return;
|
||||
loadStickers({
|
||||
stickerSetInfo: fromUserStatusEmoji.stickerSetInfo,
|
||||
});
|
||||
}, [loadStickers, fromUserStatusEmoji, fromUserStatusSet]);
|
||||
|
||||
const handleOpenStatusSet = useLastCallback(() => {
|
||||
if (!fromUserStatusSet) return;
|
||||
|
||||
openStickerSet({
|
||||
stickerSetInfo: fromUserStatusSet,
|
||||
});
|
||||
});
|
||||
|
||||
const stickerSetTitle = useMemo(() => {
|
||||
if (!fromUserStatusSet || !fromUser) return undefined;
|
||||
|
||||
const template = lang('lng_premium_emoji_status_title').replace('{user}', getUserFullName(fromUser)!);
|
||||
const [first, second] = template.split('{link}');
|
||||
|
||||
const emoji = fromUserStatusSet.thumbCustomEmojiId ? (
|
||||
<CustomEmoji className={styles.stickerSetLinkIcon} documentId={fromUserStatusSet.thumbCustomEmojiId} />
|
||||
) : undefined;
|
||||
const link = (
|
||||
<span className={styles.stickerSetLink} onClick={handleOpenStatusSet}>
|
||||
{emoji}{renderText(fromUserStatusSet.title)}
|
||||
</span>
|
||||
);
|
||||
return [renderText(first), link, renderText(second)];
|
||||
}, [fromUser, fromUserStatusSet, lang]);
|
||||
|
||||
if (!promo || (fromUserStatusEmoji && !fromUserStatusSet)) return undefined;
|
||||
|
||||
// TODO Support all subscription options
|
||||
const month = promo.options.find((option) => option.months === 1)!;
|
||||
@ -209,6 +253,10 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
: lang('TelegramPremiumUserGiftedPremiumDialogSubtitle');
|
||||
}
|
||||
|
||||
if (fromUserStatusSet) {
|
||||
return lang('TelegramPremiumUserStatusDialogSubtitle');
|
||||
}
|
||||
|
||||
return fromUser
|
||||
? lang('TelegramPremiumUserDialogSubtitle')
|
||||
: lang(isPremium ? 'TelegramPremiumSubscribedSubtitle' : 'TelegramPremiumSubtitle');
|
||||
@ -252,9 +300,19 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
<i className="icon icon-close" />
|
||||
</Button>
|
||||
<img className={styles.logo} src={PremiumLogo} alt="" />
|
||||
<h2 className={styles.headerText}>
|
||||
{renderText(getHeaderText(), ['simple_markdown', 'emoji'])}
|
||||
{fromUserStatusEmoji ? (
|
||||
<CustomEmoji
|
||||
className={styles.statusEmoji}
|
||||
onClick={handleOpenStatusSet}
|
||||
documentId={fromUserStatusEmoji.id}
|
||||
isBig
|
||||
size={STATUS_EMOJI_SIZE}
|
||||
/>
|
||||
) : (
|
||||
<img className={styles.logo} src={PremiumLogo} alt="" />
|
||||
)}
|
||||
<h2 className={buildClassName(styles.headerText, fromUserStatusSet && styles.stickerSetText)}>
|
||||
{fromUserStatusSet ? stickerSetTitle : renderText(getHeaderText(), ['simple_markdown', 'emoji'])}
|
||||
</h2>
|
||||
<div className={styles.description}>
|
||||
{renderText(getHeaderDescription(), ['simple_markdown', 'emoji'])}
|
||||
@ -297,7 +355,6 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
{!isPremium && (
|
||||
<div className={styles.footer}>
|
||||
{/* eslint-disable-next-line react/jsx-no-bind */}
|
||||
<Button className={styles.button} isShiny withPremiumGradient onClick={handleClick}>
|
||||
{lang('SubscribeToPremium', formatCurrency(Number(month.amount), month.currency, lang.code))}
|
||||
</Button>
|
||||
@ -324,6 +381,13 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
const {
|
||||
premiumModal,
|
||||
} = selectTabState(global);
|
||||
|
||||
const fromUser = premiumModal?.fromUserId ? selectUser(global, premiumModal.fromUserId) : undefined;
|
||||
const fromUserStatusEmoji = fromUser?.emojiStatus ? global.customEmojis.byId[fromUser.emojiStatus.documentId]
|
||||
: undefined;
|
||||
const fromUserStatusSet = fromUserStatusEmoji ? selectStickerSet(global, fromUserStatusEmoji.stickerSetInfo)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
currentUserId: global.currentUserId,
|
||||
promo: premiumModal?.promo,
|
||||
@ -331,7 +395,9 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
isSuccess: premiumModal?.isSuccess,
|
||||
isGift: premiumModal?.isGift,
|
||||
monthsAmount: premiumModal?.monthsAmount,
|
||||
fromUser: premiumModal?.fromUserId ? selectUser(global, premiumModal.fromUserId) : undefined,
|
||||
fromUser,
|
||||
fromUserStatusEmoji,
|
||||
fromUserStatusSet,
|
||||
toUser: premiumModal?.toUserId ? selectUser(global, premiumModal.toUserId) : undefined,
|
||||
initialSection: premiumModal?.initialSection,
|
||||
isPremium: selectIsCurrentUserPremium(global),
|
||||
|
||||
@ -149,6 +149,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
loadPinnedMessages,
|
||||
toggleLeftColumn,
|
||||
exitMessageSelectMode,
|
||||
openPremiumModal,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
@ -184,7 +185,12 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
const componentRef = useRef<HTMLDivElement>(null);
|
||||
const shouldAnimateTools = useRef<boolean>(true);
|
||||
|
||||
const { handleClick: handleHeaderClick, handleMouseDown: handleHeaderMouseDown } = useFastClick(() => {
|
||||
const {
|
||||
handleClick: handleHeaderClick,
|
||||
handleMouseDown: handleHeaderMouseDown,
|
||||
} = useFastClick((e: React.MouseEvent<HTMLDivElement | HTMLButtonElement>) => {
|
||||
if (e.type === 'mousedown' && (e.target as Element).closest('.title > .custom-emoji')) return;
|
||||
|
||||
openChatWithInfo({ id: chatId, threadId });
|
||||
});
|
||||
|
||||
@ -214,6 +220,10 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
}, BACK_BUTTON_INACTIVE_TIME);
|
||||
});
|
||||
|
||||
const handleStatusClick = useLastCallback(() => {
|
||||
openPremiumModal({ fromUserId: chatId });
|
||||
});
|
||||
|
||||
const handleBackClick = useLastCallback((e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
if (!isBackButtonActive.current) return;
|
||||
|
||||
@ -371,6 +381,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
withUpdatingStatus
|
||||
emojiStatusSize={EMOJI_STATUS_SIZE}
|
||||
noRtl
|
||||
onEmojiStatusClick={handleStatusClick}
|
||||
/>
|
||||
) : (
|
||||
<GroupChatInfo
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user