Gifts: Show more info (#5114)
This commit is contained in:
parent
ebe9377b41
commit
699dfa4a1e
@ -85,6 +85,7 @@ export interface GramJsAppConfig extends LimitsConfig {
|
||||
upload_premium_speedup_upload?: number;
|
||||
stars_gifts_enabled?: boolean;
|
||||
stargifts_message_length_max?: number;
|
||||
stargifts_convert_period_max?: number;
|
||||
}
|
||||
|
||||
function buildEmojiSounds(appConfig: GramJsAppConfig) {
|
||||
@ -169,5 +170,6 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
|
||||
isChannelRevenueWithdrawalEnabled: appConfig.channel_revenue_withdrawal_enabled,
|
||||
isStarsGiftEnabled: appConfig.stars_gifts_enabled,
|
||||
starGiftMaxMessageLength: appConfig.stargifts_message_length_max,
|
||||
starGiftMaxConvertPeriod: appConfig.stargifts_convert_period_max,
|
||||
};
|
||||
}
|
||||
|
||||
@ -585,7 +585,8 @@ export function buildApiStarTopupOption(option: GramJs.TypeStarsTopupOption): Ap
|
||||
|
||||
export function buildApiStarGift(startGift: GramJs.StarGift): ApiStarGift {
|
||||
const {
|
||||
id, limited, sticker, stars, availabilityRemains, availabilityTotal, convertStars,
|
||||
id, limited, sticker, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate,
|
||||
soldOut,
|
||||
} = startGift;
|
||||
|
||||
return {
|
||||
@ -596,6 +597,9 @@ export function buildApiStarGift(startGift: GramJs.StarGift): ApiStarGift {
|
||||
availabilityRemains,
|
||||
availabilityTotal,
|
||||
starsToConvert: convertStars.toJSNumber(),
|
||||
firstSaleDate,
|
||||
lastSaleDate,
|
||||
isSoldOut: soldOut,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -237,6 +237,7 @@ export interface ApiAppConfig {
|
||||
isChannelRevenueWithdrawalEnabled?: boolean;
|
||||
isStarsGiftEnabled?: boolean;
|
||||
starGiftMaxMessageLength?: number;
|
||||
starGiftMaxConvertPeriod?: number;
|
||||
}
|
||||
|
||||
export interface ApiConfig {
|
||||
|
||||
@ -196,6 +196,9 @@ export type ApiStarGift = {
|
||||
availabilityRemains?: number;
|
||||
availabilityTotal?: number;
|
||||
starsToConvert: number;
|
||||
isSoldOut?: true;
|
||||
firstSaleDate?: number;
|
||||
lastSaleDate?: number;
|
||||
};
|
||||
|
||||
export interface ApiUserStarGift {
|
||||
|
||||
@ -1296,7 +1296,6 @@
|
||||
"GiftSoldCount" = "{count} sold";
|
||||
"GiftLeftCount" = "{count} left";
|
||||
"GiftSoldOut" = "sold out";
|
||||
"GiftSoldOutInfo" = "Sorry, this gift is sold out.";
|
||||
"GiftMessagePlaceholder" = "Enter Message (Optional)";
|
||||
"GiftHideMyName" = "Hide My Name";
|
||||
"GiftHideNameDescription" = "Hide my name and message from visitors to {profile}'s profile. {receiver} will still see your name and message.";
|
||||
@ -1320,10 +1319,21 @@
|
||||
"GiftInfoConvert_one" = "Convert to {amount} Star";
|
||||
"GiftInfoConvert_other" = "Convert to {amount} Stars";
|
||||
"GiftInfoConvertTitle" = "Convert Gift to Stars";
|
||||
"GiftInfoConvertDescription" = "Do you want to convert this gift from **{user}** to **{amount}**?\n\nThis action cannot be undone. This will permanently destroy the gift.";
|
||||
"GiftInfoConvertDescription1" = "Do you want to convert this gift from **{user}** to **{amount}**?";
|
||||
"GiftInfoConvertDescription2" = "This action cannot be undone. This will permanently destroy the gift.";
|
||||
"GiftInfoConvertDescriptionPeriod_one" = "Conversion is available for the next **{count} days**."
|
||||
"GiftInfoConvertDescriptionPeriod_other" = "Conversion is available for the next **{count} days**."
|
||||
"GiftInfoSaved" = "This gift is visible on your profile. {link}";
|
||||
"GiftInfoSavedView" = "View >";
|
||||
"GiftInfoHidden" = "This gift is hidden. Only you can see it.";
|
||||
"GiftInfoAvailability" = "Availability";
|
||||
"GiftInfoAvailabilityValue_one" = "{count} of {total} left";
|
||||
"GiftInfoAvailabilityValue_other" = "{count} of {total} left";
|
||||
"GiftInfoFirstSale" = "First Sale";
|
||||
"GiftInfoLastSale" = "Last Sale";
|
||||
"GiftInfoSoldOutTitle" = "Unavailable";
|
||||
"GiftInfoSoldOutDescription" = "This gift has been sold out";
|
||||
"GiftInfoSenderHidden" = "Only you can see the sender's name and message.";
|
||||
"StarsAmount" = "⭐️{amount}";
|
||||
"StarsAmountText_one" = "{amount} Star";
|
||||
"StarsAmountText_other" = "{amount} Stars";
|
||||
|
||||
@ -30,21 +30,18 @@ export type StateProps = {
|
||||
const GIFT_STICKER_SIZE = 90;
|
||||
|
||||
function GiftItemStar({ sticker, gift, onClick }: OwnProps & StateProps) {
|
||||
const { showNotification } = getActions();
|
||||
const { openGiftInfoModal } = getActions();
|
||||
const lang = useLang();
|
||||
|
||||
const {
|
||||
stars,
|
||||
isLimited,
|
||||
availabilityRemains,
|
||||
availabilityTotal,
|
||||
isSoldOut,
|
||||
} = gift;
|
||||
|
||||
const isSoldOut = availabilityTotal && !availabilityRemains;
|
||||
|
||||
const handleGiftClick = useLastCallback(() => {
|
||||
if (isSoldOut) {
|
||||
showNotification({ message: lang('GiftSoldOutInfo') });
|
||||
openGiftInfoModal({ gift });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,10 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.soldOut {
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import type { TabState } from '../../../../global/types';
|
||||
import { STARS_ICON_PLACEHOLDER } from '../../../../config';
|
||||
import { getUserFullName } from '../../../../global/helpers';
|
||||
import { selectStarGiftSticker, selectUser } from '../../../../global/selectors';
|
||||
import buildClassName from '../../../../util/buildClassName';
|
||||
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
||||
import { CUSTOM_PEER_HIDDEN } from '../../../../util/objects/customPeer';
|
||||
import { formatInteger } from '../../../../util/textFormat';
|
||||
@ -38,12 +39,13 @@ type StateProps = {
|
||||
userFrom?: ApiUser;
|
||||
targetUser?: ApiUser;
|
||||
currentUserId?: string;
|
||||
starGiftMaxConvertPeriod?: number;
|
||||
};
|
||||
|
||||
const STICKER_SIZE = 120;
|
||||
|
||||
const GiftInfoModal = ({
|
||||
modal, sticker, userFrom, targetUser, currentUserId,
|
||||
modal, sticker, userFrom, targetUser, currentUserId, starGiftMaxConvertPeriod,
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
closeGiftInfoModal,
|
||||
@ -59,9 +61,14 @@ const GiftInfoModal = ({
|
||||
|
||||
const isOpen = Boolean(modal);
|
||||
const renderingModal = useCurrentOrPrev(modal);
|
||||
const { gift: userGift } = renderingModal || {};
|
||||
const { gift: typeGift } = renderingModal || {};
|
||||
const isUserGift = typeGift && 'gift' in typeGift;
|
||||
const userGift = isUserGift ? typeGift : undefined;
|
||||
const canUpdate = Boolean(userGift?.fromId && userGift.messageId);
|
||||
const isSender = userGift?.fromId === currentUserId;
|
||||
const canConvertDifference = (userGift && starGiftMaxConvertPeriod && (
|
||||
userGift.date + starGiftMaxConvertPeriod - Date.now() / 1000
|
||||
)) || 0;
|
||||
|
||||
const handleClose = useLastCallback(() => {
|
||||
closeGiftInfoModal();
|
||||
@ -86,16 +93,23 @@ const GiftInfoModal = ({
|
||||
});
|
||||
|
||||
const modalData = useMemo(() => {
|
||||
if (!userGift) {
|
||||
if (!typeGift) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const {
|
||||
gift, date, fromId, isNameHidden, message, starsToConvert, isUnsaved, isConverted,
|
||||
} = userGift;
|
||||
fromId, isNameHidden, message, starsToConvert, isUnsaved, isConverted,
|
||||
} = userGift || {};
|
||||
const gift = isUserGift ? typeGift.gift : typeGift;
|
||||
|
||||
const isVisibleForMe = isNameHidden && targetUser;
|
||||
|
||||
const description = (() => {
|
||||
if (!userGift) {
|
||||
return lang('GiftInfoSoldOutDescription');
|
||||
}
|
||||
if (!canUpdate && !isSender) return undefined;
|
||||
if (!starsToConvert || canConvertDifference < 0) return undefined;
|
||||
if (isConverted) {
|
||||
return canUpdate
|
||||
? lang('GiftInfoDescriptionConverted', {
|
||||
@ -135,16 +149,19 @@ const GiftInfoModal = ({
|
||||
<div className={styles.header}>
|
||||
<AnimatedIconFromSticker sticker={sticker} noLoop nonInteractive size={STICKER_SIZE} />
|
||||
<h1 className={styles.title}>
|
||||
{lang(canUpdate ? 'GiftInfoReceived' : 'GiftInfoTitle')}
|
||||
{!userGift && lang('GiftInfoSoldOutTitle')}
|
||||
{userGift && lang(canUpdate ? 'GiftInfoReceived' : 'GiftInfoTitle')}
|
||||
</h1>
|
||||
<p className={styles.amount}>
|
||||
<span className={styles.amount}>
|
||||
{formatInteger(gift.stars)}
|
||||
</span>
|
||||
<StarIcon type="gold" size="middle" />
|
||||
</p>
|
||||
{userGift && (
|
||||
<p className={styles.amount}>
|
||||
<span className={styles.amount}>
|
||||
{formatInteger(gift.stars)}
|
||||
</span>
|
||||
<StarIcon type="gold" size="middle" />
|
||||
</p>
|
||||
)}
|
||||
{description && (
|
||||
<p className={styles.description}>
|
||||
<p className={buildClassName(styles.description, !userGift && styles.soldOut)}>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
@ -164,10 +181,26 @@ const GiftInfoModal = ({
|
||||
]);
|
||||
}
|
||||
|
||||
tableData.push([
|
||||
lang('GiftInfoDate'),
|
||||
formatDateTimeToString(date * 1000, lang.code, true),
|
||||
]);
|
||||
if (userGift?.date) {
|
||||
tableData.push([
|
||||
lang('GiftInfoDate'),
|
||||
formatDateTimeToString(userGift.date * 1000, lang.code, true),
|
||||
]);
|
||||
}
|
||||
|
||||
if (gift.firstSaleDate) {
|
||||
tableData.push([
|
||||
lang('GiftInfoFirstSale'),
|
||||
formatDateTimeToString(gift.firstSaleDate * 1000, lang.code, true),
|
||||
]);
|
||||
}
|
||||
|
||||
if (gift.lastSaleDate) {
|
||||
tableData.push([
|
||||
lang('GiftInfoLastSale'),
|
||||
formatDateTimeToString(gift.lastSaleDate * 1000, lang.code, true),
|
||||
]);
|
||||
}
|
||||
|
||||
tableData.push([
|
||||
lang('GiftInfoValue'),
|
||||
@ -180,7 +213,7 @@ const GiftInfoModal = ({
|
||||
[STARS_ICON_PLACEHOLDER]: <StarIcon type="gold" size="small" />,
|
||||
},
|
||||
})}
|
||||
{canUpdate && Boolean(starsToConvert) && (
|
||||
{canUpdate && canConvertDifference > 0 && Boolean(starsToConvert) && (
|
||||
<BadgeButton onClick={openConvertConfirm}>
|
||||
{lang('GiftInfoConvert', { amount: starsToConvert }, { pluralValue: starsToConvert })}
|
||||
</BadgeButton>
|
||||
@ -188,6 +221,16 @@ const GiftInfoModal = ({
|
||||
</div>,
|
||||
]);
|
||||
|
||||
if (gift.availabilityTotal) {
|
||||
tableData.push([
|
||||
lang('GiftInfoAvailability'),
|
||||
lang('GiftInfoAvailabilityValue', {
|
||||
count: formatInteger(gift.availabilityRemains!),
|
||||
total: formatInteger(gift.availabilityTotal),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
if (message) {
|
||||
tableData.push([
|
||||
undefined,
|
||||
@ -198,14 +241,21 @@ const GiftInfoModal = ({
|
||||
const footer = (
|
||||
<div className={styles.footer}>
|
||||
{canUpdate && (
|
||||
<p className={styles.footerDescription}>
|
||||
{isUnsaved ? lang('GiftInfoHidden')
|
||||
: lang('GiftInfoSaved', {
|
||||
link: <Link isPrimary onClick={handleOpenProfile}>{lang('GiftInfoSavedView')}</Link>,
|
||||
}, {
|
||||
withNodes: true,
|
||||
})}
|
||||
</p>
|
||||
<div className={styles.footerDescription}>
|
||||
<div>
|
||||
{isUnsaved ? lang('GiftInfoHidden')
|
||||
: lang('GiftInfoSaved', {
|
||||
link: <Link isPrimary onClick={handleOpenProfile}>{lang('GiftInfoSavedView')}</Link>,
|
||||
}, {
|
||||
withNodes: true,
|
||||
})}
|
||||
</div>
|
||||
{isVisibleForMe && (
|
||||
<div>
|
||||
{lang('GiftInfoSenderHidden')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!canUpdate && (
|
||||
<Button size="smaller" onClick={handleClose}>
|
||||
@ -225,7 +275,7 @@ const GiftInfoModal = ({
|
||||
tableData,
|
||||
footer,
|
||||
};
|
||||
}, [userGift, sticker, lang, canUpdate, isSender, oldLang, targetUser]);
|
||||
}, [typeGift, userGift, isUserGift, targetUser, sticker, lang, canUpdate, canConvertDifference, isSender, oldLang]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -236,31 +286,48 @@ const GiftInfoModal = ({
|
||||
footer={modalData?.footer}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
<ConfirmDialog
|
||||
isOpen={isConvertConfirmOpen}
|
||||
onClose={closeConvertConfirm}
|
||||
confirmHandler={handleConvertToStars}
|
||||
title={lang('GiftInfoConvertTitle')}
|
||||
>
|
||||
{userGift && lang('GiftInfoConvertDescription', {
|
||||
amount: lang('StarsAmountText', { amount: formatInteger(userGift.starsToConvert!) }),
|
||||
user: getUserFullName(userFrom)!,
|
||||
}, {
|
||||
withNodes: true,
|
||||
withMarkdown: true,
|
||||
renderTextFilters: ['br'],
|
||||
})}
|
||||
</ConfirmDialog>
|
||||
{userGift && (
|
||||
<ConfirmDialog
|
||||
isOpen={isConvertConfirmOpen}
|
||||
onClose={closeConvertConfirm}
|
||||
confirmHandler={handleConvertToStars}
|
||||
title={lang('GiftInfoConvertTitle')}
|
||||
>
|
||||
<div>
|
||||
{lang('GiftInfoConvertDescription1', {
|
||||
amount: lang('StarsAmountText', { amount: formatInteger(userGift.starsToConvert!) }),
|
||||
user: getUserFullName(userFrom)!,
|
||||
}, {
|
||||
withNodes: true,
|
||||
withMarkdown: true,
|
||||
})}
|
||||
</div>
|
||||
{canConvertDifference > 0 && (
|
||||
<div>
|
||||
{lang('GiftInfoConvertDescriptionPeriod', {
|
||||
count: formatInteger(Math.ceil(canConvertDifference / 60 / 60 / 24)),
|
||||
}, {
|
||||
withNodes: true,
|
||||
withMarkdown: true,
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div>{lang('GiftInfoConvertDescription2')}</div>
|
||||
</ConfirmDialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { modal }): StateProps => {
|
||||
const stickerId = modal?.gift?.gift.stickerId;
|
||||
const typeGift = modal?.gift;
|
||||
const isUserGift = typeGift && 'gift' in typeGift;
|
||||
const gift = isUserGift ? typeGift.gift : typeGift;
|
||||
const stickerId = gift?.stickerId;
|
||||
const sticker = stickerId ? selectStarGiftSticker(global, stickerId) : undefined;
|
||||
|
||||
const fromId = modal?.gift?.fromId;
|
||||
const fromId = isUserGift && typeGift.fromId;
|
||||
const userFrom = fromId ? selectUser(global, fromId) : undefined;
|
||||
const targetUser = modal?.userId ? selectUser(global, modal.userId) : undefined;
|
||||
|
||||
@ -269,6 +336,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
userFrom,
|
||||
targetUser,
|
||||
currentUserId: global.currentUserId,
|
||||
starGiftMaxConvertPeriod: global.appConfig?.starGiftMaxConvertPeriod,
|
||||
};
|
||||
},
|
||||
)(GiftInfoModal));
|
||||
|
||||
@ -264,9 +264,11 @@ addActionHandler('openGiftInfoModalFromMessage', (global, actions, payload): Act
|
||||
|
||||
addActionHandler('openGiftInfoModal', (global, actions, payload): ActionReturnType => {
|
||||
const {
|
||||
userId, gift, tabId = getCurrentTabId(),
|
||||
gift, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
const userId = 'userId' in payload ? payload.userId : undefined;
|
||||
|
||||
return updateTabState(global, {
|
||||
giftInfoModal: {
|
||||
userId,
|
||||
|
||||
@ -882,8 +882,8 @@ export type TabState = {
|
||||
};
|
||||
|
||||
giftInfoModal?: {
|
||||
userId: string;
|
||||
gift: ApiUserStarGift;
|
||||
userId?: string;
|
||||
gift: ApiUserStarGift | ApiStarGift;
|
||||
};
|
||||
};
|
||||
|
||||
@ -3461,10 +3461,12 @@ export interface ActionPayloads {
|
||||
chatId: string;
|
||||
messageId: number;
|
||||
} & WithTabId;
|
||||
openGiftInfoModal: {
|
||||
openGiftInfoModal: ({
|
||||
userId: string;
|
||||
gift: ApiUserStarGift;
|
||||
} & WithTabId;
|
||||
} | {
|
||||
gift: ApiStarGift;
|
||||
}) & WithTabId;
|
||||
closeGiftInfoModal: WithTabId | undefined;
|
||||
loadUserGifts: {
|
||||
userId: string;
|
||||
|
||||
17
src/types/language.d.ts
vendored
17
src/types/language.d.ts
vendored
@ -1563,7 +1563,6 @@ export interface LangPair {
|
||||
'count': string | number;
|
||||
};
|
||||
'GiftSoldOut': undefined;
|
||||
'GiftSoldOutInfo': undefined;
|
||||
'GiftMessagePlaceholder': undefined;
|
||||
'GiftHideMyName': undefined;
|
||||
'GiftHideNameDescription': {
|
||||
@ -1599,15 +1598,29 @@ export interface LangPair {
|
||||
'amount': string | number;
|
||||
};
|
||||
'GiftInfoConvertTitle': undefined;
|
||||
'GiftInfoConvertDescription': {
|
||||
'GiftInfoConvertDescription1': {
|
||||
'user': string | number;
|
||||
'amount': string | number;
|
||||
};
|
||||
'GiftInfoConvertDescription2': undefined;
|
||||
'GiftInfoConvertDescriptionPeriod': {
|
||||
'count': string | number;
|
||||
};
|
||||
'GiftInfoSaved': {
|
||||
'link': string | number;
|
||||
};
|
||||
'GiftInfoSavedView': undefined;
|
||||
'GiftInfoHidden': undefined;
|
||||
'GiftInfoAvailability': undefined;
|
||||
'GiftInfoAvailabilityValue': {
|
||||
'count': string | number;
|
||||
'total': string | number;
|
||||
};
|
||||
'GiftInfoFirstSale': undefined;
|
||||
'GiftInfoLastSale': undefined;
|
||||
'GiftInfoSoldOutTitle': undefined;
|
||||
'GiftInfoSoldOutDescription': undefined;
|
||||
'GiftInfoSenderHidden': undefined;
|
||||
'StarsAmount': {
|
||||
'amount': string | number;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user