Gifts: Support unique links for gifts (#5450)
This commit is contained in:
parent
93cb31c980
commit
86cb0f5ee6
141
src/api/gramjs/apiBuilders/gifts.ts
Normal file
141
src/api/gramjs/apiBuilders/gifts.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import type {
|
||||
ApiStarGift,
|
||||
ApiStarGiftAttribute,
|
||||
ApiUserStarGift,
|
||||
} from '../../types';
|
||||
|
||||
import { numberToHexColor } from '../../../util/colors';
|
||||
import { addDocumentToLocalDb } from '../helpers';
|
||||
import { buildApiFormattedText } from './common';
|
||||
import { buildApiPeerId } from './peers';
|
||||
import { buildStickerFromDocument } from './symbols';
|
||||
|
||||
export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
||||
if (starGift instanceof GramJs.StarGiftUnique) {
|
||||
const {
|
||||
id, num, ownerId, ownerName, title, attributes, availabilityIssued, availabilityTotal,
|
||||
} = starGift;
|
||||
|
||||
return {
|
||||
type: 'starGiftUnique',
|
||||
id: id.toString(),
|
||||
number: num,
|
||||
ownerId: ownerId && buildApiPeerId(ownerId, 'user'),
|
||||
ownerName,
|
||||
attributes: attributes.map(buildApiStarGiftAttribute).filter(Boolean),
|
||||
title,
|
||||
totalCount: availabilityTotal,
|
||||
issuedCount: availabilityIssued,
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
id, limited, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate, soldOut,
|
||||
birthday, upgradeStars,
|
||||
} = starGift;
|
||||
|
||||
addDocumentToLocalDb(starGift.sticker);
|
||||
|
||||
const sticker = buildStickerFromDocument(starGift.sticker)!;
|
||||
|
||||
return {
|
||||
type: 'starGift',
|
||||
id: id.toString(),
|
||||
isLimited: limited,
|
||||
sticker,
|
||||
stars: stars.toJSNumber(),
|
||||
availabilityRemains,
|
||||
availabilityTotal,
|
||||
starsToConvert: convertStars.toJSNumber(),
|
||||
firstSaleDate,
|
||||
lastSaleDate,
|
||||
isSoldOut: soldOut,
|
||||
isBirthday: birthday,
|
||||
upgradeStars: upgradeStars?.toJSNumber(),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiStarGiftAttribute(attribute: GramJs.TypeStarGiftAttribute): ApiStarGiftAttribute | undefined {
|
||||
if (attribute instanceof GramJs.StarGiftAttributeModel) {
|
||||
const sticker = buildStickerFromDocument(attribute.document);
|
||||
if (!sticker) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addDocumentToLocalDb(attribute.document);
|
||||
|
||||
return {
|
||||
type: 'model',
|
||||
name: attribute.name,
|
||||
rarityPercent: attribute.rarityPermille / 10,
|
||||
sticker,
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute instanceof GramJs.StarGiftAttributePattern) {
|
||||
const sticker = buildStickerFromDocument(attribute.document);
|
||||
if (!sticker) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addDocumentToLocalDb(attribute.document);
|
||||
|
||||
return {
|
||||
type: 'pattern',
|
||||
name: attribute.name,
|
||||
rarityPercent: attribute.rarityPermille / 10,
|
||||
sticker,
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute instanceof GramJs.StarGiftAttributeBackdrop) {
|
||||
const {
|
||||
name, rarityPermille, centerColor, edgeColor, patternColor, textColor,
|
||||
} = attribute;
|
||||
|
||||
return {
|
||||
type: 'backdrop',
|
||||
name,
|
||||
rarityPercent: rarityPermille / 10,
|
||||
centerColor: numberToHexColor(centerColor),
|
||||
edgeColor: numberToHexColor(edgeColor),
|
||||
patternColor: numberToHexColor(patternColor),
|
||||
textColor: numberToHexColor(textColor),
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute instanceof GramJs.StarGiftAttributeOriginalDetails) {
|
||||
const {
|
||||
date, recipientId, message, senderId,
|
||||
} = attribute;
|
||||
|
||||
return {
|
||||
type: 'originalDetails',
|
||||
date,
|
||||
recipientId: recipientId && buildApiPeerId(recipientId, 'user'),
|
||||
message: message && buildApiFormattedText(message),
|
||||
senderId: senderId && buildApiPeerId(senderId, 'user'),
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function buildApiUserStarGift(userStarGift: GramJs.UserStarGift): ApiUserStarGift {
|
||||
const {
|
||||
gift, date, convertStars, fromId, message, msgId, nameHidden, unsaved,
|
||||
} = userStarGift;
|
||||
|
||||
return {
|
||||
gift: buildApiStarGift(gift),
|
||||
date,
|
||||
starsToConvert: convertStars?.toJSNumber(),
|
||||
fromId: fromId && buildApiPeerId(fromId, 'user'),
|
||||
message: message && buildApiFormattedText(message),
|
||||
messageId: msgId,
|
||||
isNameHidden: nameHidden,
|
||||
isUnsaved: unsaved,
|
||||
};
|
||||
}
|
||||
@ -15,6 +15,7 @@ import type {
|
||||
ApiPaidMedia,
|
||||
ApiPhoto,
|
||||
ApiPoll,
|
||||
ApiStarGiftUnique,
|
||||
ApiSticker,
|
||||
ApiVideo,
|
||||
ApiVoice,
|
||||
@ -42,6 +43,7 @@ import {
|
||||
buildApiThumbnailFromPath,
|
||||
buildApiThumbnailFromStripped,
|
||||
} from './common';
|
||||
import { buildApiStarGift } from './gifts';
|
||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||
import { buildStickerFromDocument, processStickerResult } from './symbols';
|
||||
|
||||
@ -753,9 +755,12 @@ export function buildWebPage(media: GramJs.TypeMessageMedia): ApiWebPage | undef
|
||||
audio = buildAudioFromDocument(document);
|
||||
}
|
||||
let story: ApiWebPageStoryData | undefined;
|
||||
let gift: ApiStarGiftUnique | undefined;
|
||||
let stickers: ApiWebPageStickerData | undefined;
|
||||
const attributeStory = attributes
|
||||
?.find((a): a is GramJs.WebPageAttributeStory => a instanceof GramJs.WebPageAttributeStory);
|
||||
const attributeGift = attributes
|
||||
?.find((a): a is GramJs.WebPageAttributeUniqueStarGift => a instanceof GramJs.WebPageAttributeUniqueStarGift);
|
||||
if (attributeStory) {
|
||||
const peerId = getApiChatIdFromMtpPeer(attributeStory.peer);
|
||||
story = {
|
||||
@ -767,6 +772,10 @@ export function buildWebPage(media: GramJs.TypeMessageMedia): ApiWebPage | undef
|
||||
addStoryToLocalDb(attributeStory.story, peerId);
|
||||
}
|
||||
}
|
||||
if (attributeGift) {
|
||||
const starGift = buildApiStarGift(attributeGift.gift);
|
||||
gift = starGift.type === 'starGiftUnique' ? starGift : undefined;
|
||||
}
|
||||
const attributeStickers = attributes?.find((a): a is GramJs.WebPageAttributeStickerSet => (
|
||||
a instanceof GramJs.WebPageAttributeStickerSet
|
||||
));
|
||||
@ -798,6 +807,7 @@ export function buildWebPage(media: GramJs.TypeMessageMedia): ApiWebPage | undef
|
||||
video,
|
||||
audio,
|
||||
story,
|
||||
gift,
|
||||
stickers,
|
||||
mediaSize,
|
||||
};
|
||||
|
||||
@ -63,8 +63,8 @@ import {
|
||||
buildApiFormattedText,
|
||||
buildApiPhoto,
|
||||
} from './common';
|
||||
import { buildApiStarGift } from './gifts';
|
||||
import { buildMessageContent, buildMessageMediaContent, buildMessageTextContent } from './messageContent';
|
||||
import { buildApiStarGift } from './payments';
|
||||
import { buildApiPeerColor, buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||
import { buildMessageReactions } from './reactions';
|
||||
|
||||
|
||||
@ -19,8 +19,6 @@ import type {
|
||||
ApiPrepaidGiveaway,
|
||||
ApiPrepaidStarsGiveaway,
|
||||
ApiReceipt,
|
||||
ApiStarGift,
|
||||
ApiStarGiftAttribute,
|
||||
ApiStarGiveawayOption,
|
||||
ApiStarsAmount,
|
||||
ApiStarsGiveawayWinnerOption,
|
||||
@ -28,19 +26,17 @@ import type {
|
||||
ApiStarsTransaction,
|
||||
ApiStarsTransactionPeer,
|
||||
ApiStarTopupOption,
|
||||
ApiUserStarGift,
|
||||
BoughtPaidMedia,
|
||||
} from '../../types';
|
||||
|
||||
import { numberToHexColor } from '../../../util/colors';
|
||||
import { addDocumentToLocalDb, addWebDocumentToLocalDb } from '../helpers';
|
||||
import { addWebDocumentToLocalDb } from '../helpers';
|
||||
import { buildApiStarsSubscriptionPricing } from './chats';
|
||||
import { buildApiFormattedText, buildApiMessageEntity } from './common';
|
||||
import { buildApiMessageEntity } from './common';
|
||||
import { buildApiStarGift } from './gifts';
|
||||
import { omitVirtualClassFields } from './helpers';
|
||||
import { buildApiDocument, buildApiWebDocument, buildMessageMediaContent } from './messageContent';
|
||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||
import { buildStatisticsPercentage } from './statistics';
|
||||
import { buildStickerFromDocument } from './symbols';
|
||||
|
||||
export function buildShippingOptions(shippingOptions: GramJs.ShippingOption[] | undefined) {
|
||||
if (!shippingOptions) {
|
||||
@ -612,131 +608,3 @@ export function buildApiStarTopupOption(option: GramJs.TypeStarsTopupOption): Ap
|
||||
isExtended: extended,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
||||
if (starGift instanceof GramJs.StarGiftUnique) {
|
||||
const {
|
||||
id, num, ownerId, ownerName, title, attributes, availabilityIssued, availabilityTotal,
|
||||
} = starGift;
|
||||
|
||||
return {
|
||||
type: 'starGiftUnique',
|
||||
id: id.toString(),
|
||||
number: num,
|
||||
ownerId: ownerId && buildApiPeerId(ownerId, 'user'),
|
||||
ownerName,
|
||||
attributes: attributes.map(buildApiStarGiftAttribute).filter(Boolean),
|
||||
title,
|
||||
totalCount: availabilityTotal,
|
||||
issuedCount: availabilityIssued,
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
id, limited, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate, soldOut,
|
||||
birthday, upgradeStars,
|
||||
} = starGift;
|
||||
|
||||
addDocumentToLocalDb(starGift.sticker);
|
||||
|
||||
const sticker = buildStickerFromDocument(starGift.sticker)!;
|
||||
|
||||
return {
|
||||
type: 'starGift',
|
||||
id: id.toString(),
|
||||
isLimited: limited,
|
||||
sticker,
|
||||
stars: stars.toJSNumber(),
|
||||
availabilityRemains,
|
||||
availabilityTotal,
|
||||
starsToConvert: convertStars.toJSNumber(),
|
||||
firstSaleDate,
|
||||
lastSaleDate,
|
||||
isSoldOut: soldOut,
|
||||
isBirthday: birthday,
|
||||
upgradeStars: upgradeStars?.toJSNumber(),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiStarGiftAttribute(attribute: GramJs.TypeStarGiftAttribute): ApiStarGiftAttribute | undefined {
|
||||
if (attribute instanceof GramJs.StarGiftAttributeModel) {
|
||||
const sticker = buildStickerFromDocument(attribute.document);
|
||||
if (!sticker) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addDocumentToLocalDb(attribute.document);
|
||||
|
||||
return {
|
||||
type: 'model',
|
||||
name: attribute.name,
|
||||
rarityPercent: attribute.rarityPermille / 10,
|
||||
sticker,
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute instanceof GramJs.StarGiftAttributePattern) {
|
||||
const sticker = buildStickerFromDocument(attribute.document);
|
||||
if (!sticker) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addDocumentToLocalDb(attribute.document);
|
||||
|
||||
return {
|
||||
type: 'pattern',
|
||||
name: attribute.name,
|
||||
rarityPercent: attribute.rarityPermille / 10,
|
||||
sticker,
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute instanceof GramJs.StarGiftAttributeBackdrop) {
|
||||
const {
|
||||
name, rarityPermille, centerColor, edgeColor, patternColor, textColor,
|
||||
} = attribute;
|
||||
|
||||
return {
|
||||
type: 'backdrop',
|
||||
name,
|
||||
rarityPercent: rarityPermille / 10,
|
||||
centerColor: numberToHexColor(centerColor),
|
||||
edgeColor: numberToHexColor(edgeColor),
|
||||
patternColor: numberToHexColor(patternColor),
|
||||
textColor: numberToHexColor(textColor),
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute instanceof GramJs.StarGiftAttributeOriginalDetails) {
|
||||
const {
|
||||
date, recipientId, message, senderId,
|
||||
} = attribute;
|
||||
|
||||
return {
|
||||
type: 'originalDetails',
|
||||
date,
|
||||
recipientId: recipientId && buildApiPeerId(recipientId, 'user'),
|
||||
message: message && buildApiFormattedText(message),
|
||||
senderId: senderId && buildApiPeerId(senderId, 'user'),
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function buildApiUserStarGift(userStarGift: GramJs.UserStarGift): ApiUserStarGift {
|
||||
const {
|
||||
gift, date, convertStars, fromId, message, msgId, nameHidden, unsaved,
|
||||
} = userStarGift;
|
||||
|
||||
return {
|
||||
gift: buildApiStarGift(gift),
|
||||
date,
|
||||
starsToConvert: convertStars?.toJSNumber(),
|
||||
fromId: fromId && buildApiPeerId(fromId, 'user'),
|
||||
message: message && buildApiFormattedText(message),
|
||||
messageId: msgId,
|
||||
isNameHidden: nameHidden,
|
||||
isUnsaved: unsaved,
|
||||
};
|
||||
}
|
||||
|
||||
@ -12,6 +12,10 @@ import type {
|
||||
} from '../../types';
|
||||
|
||||
import { DEBUG } from '../../../config';
|
||||
import {
|
||||
buildApiStarGift,
|
||||
buildApiUserStarGift,
|
||||
} from '../apiBuilders/gifts';
|
||||
import {
|
||||
buildApiBoost,
|
||||
buildApiBoostsStatus,
|
||||
@ -22,14 +26,12 @@ import {
|
||||
buildApiPremiumGiftCodeOption,
|
||||
buildApiPremiumPromo,
|
||||
buildApiReceipt,
|
||||
buildApiStarGift,
|
||||
buildApiStarsAmount,
|
||||
buildApiStarsGiftOptions,
|
||||
buildApiStarsGiveawayOptions,
|
||||
buildApiStarsSubscription,
|
||||
buildApiStarsTransaction,
|
||||
buildApiStarTopupOption,
|
||||
buildApiUserStarGift,
|
||||
buildShippingOptions,
|
||||
} from '../apiBuilders/payments';
|
||||
import { buildApiPeerId } from '../apiBuilders/peers';
|
||||
@ -638,3 +640,15 @@ export async function fetchStarsTopupOptions() {
|
||||
|
||||
return result.map(buildApiStarTopupOption);
|
||||
}
|
||||
|
||||
export async function fetchUniqueStarGift({ slug }: {
|
||||
slug: string;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.payments.GetUniqueStarGift({ slug }));
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
const gift = buildApiStarGift(result.gift);
|
||||
return gift.type === 'starGiftUnique' ? gift : undefined;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import type {
|
||||
ApiLabeledPrice,
|
||||
ApiPremiumGiftCodeOption,
|
||||
ApiStarGift,
|
||||
ApiStarGiftUnique,
|
||||
} from './payments';
|
||||
import type {
|
||||
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
||||
@ -543,6 +544,7 @@ export interface ApiWebPage {
|
||||
document?: ApiDocument;
|
||||
video?: ApiVideo;
|
||||
story?: ApiWebPageStoryData;
|
||||
gift?: ApiStarGiftUnique;
|
||||
stickers?: ApiWebPageStickerData;
|
||||
mediaSize?: WebPageMediaSize;
|
||||
hasLargeMedia?: boolean;
|
||||
|
||||
@ -1494,3 +1494,19 @@
|
||||
"ActionUnsupportedTitle" = "Action not supported yet";
|
||||
"ActionUnsupportedDescription" = "Please, use one of our apps to complete this action.";
|
||||
"LocationPermissionText" = "**{name}** requests access to set your **location**. You will be able to revoke this access in the profile page of **{name}**.";
|
||||
"GiftWasNotFound" = "Gift was not found";
|
||||
"ViewButtonRequestJoin" = "REQUEST TO JOIN";
|
||||
"ViewButtonMessage" = "VIEW MESSAGE";
|
||||
"ViewButtonBot" = "VIEW BOT";
|
||||
"ViewButtonVoiceChat" = "VOICE CHAT";
|
||||
"ViewButtonVoiceChatChannel" = "LIVE STREAM";
|
||||
"ViewButtonGroup" = "VIEW GROUP";
|
||||
"ViewButtonChannel" = "VIEW CHANNEL";
|
||||
"ViewButtonUser" = "SEND MESSAGE";
|
||||
"ViewButtonBotApp" = "LAUNCH";
|
||||
"ViewChatList" = "VIEW CHAT LIST";
|
||||
"ViewButtonStory" = "VIEW STORY";
|
||||
"ViewButtonBoost" = "BOOST";
|
||||
"ViewButtonStickerset" = "VIEW STICKERS";
|
||||
"ViewButtonGiftUnique" = "VIEW COLLECTIBLE";
|
||||
|
||||
|
||||
@ -277,6 +277,7 @@
|
||||
max-width: 17rem;
|
||||
}
|
||||
|
||||
.web-page-gift,
|
||||
.action-message-gift {
|
||||
display: flex !important;
|
||||
width: 13.75rem;
|
||||
@ -290,10 +291,21 @@
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.web-page-gift {
|
||||
position: relative;
|
||||
min-width: 12.5rem;
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
padding-block: 2rem !important;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.web-page-centered,
|
||||
.action-message-centered {
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.web-page-unique,
|
||||
.action-message-unique {
|
||||
&::before {
|
||||
content: "";
|
||||
@ -305,6 +317,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.web-page-unique-background-wrapper,
|
||||
.action-message-unique-background-wrapper {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@ -312,11 +325,15 @@
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.web-page-unique-background,
|
||||
.action-message-unique-background {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
top: -6rem;
|
||||
}
|
||||
.web-page-unique-background {
|
||||
top: -1rem;
|
||||
}
|
||||
|
||||
.action-message-user-caption,
|
||||
.action-message-stars-balance {
|
||||
|
||||
@ -34,6 +34,14 @@
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
&--unique-sticker {
|
||||
position: relative;
|
||||
width: 7.5rem;
|
||||
height: 7.5rem;
|
||||
overflow: hidden;
|
||||
margin-block: 0.5rem;
|
||||
}
|
||||
|
||||
&--stickers {
|
||||
color: var(--accent-color);
|
||||
border-radius: 0 !important;
|
||||
@ -60,6 +68,7 @@
|
||||
.WebPage--content {
|
||||
position: relative;
|
||||
|
||||
&.is-gift,
|
||||
&.is-story {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
@ -85,6 +94,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.with-gift &--quick-button {
|
||||
border-top: inherit;
|
||||
margin-top: 0.0625rem;
|
||||
margin-bottom: -0.125rem;
|
||||
}
|
||||
|
||||
&-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -1,21 +1,24 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useRef } from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../global';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiMessage, ApiTypeStory } from '../../../api/types';
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
import { AudioOrigin, type ISettings } from '../../../types';
|
||||
|
||||
import { getMessageWebPage } from '../../../global/helpers';
|
||||
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import trimText from '../../../util/trimText';
|
||||
import { getGiftAttributes, getStickerFromGift } from '../../common/helpers/gifts';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import { calculateMediaDimensions } from './helpers/mediaDimensions';
|
||||
import { getWebpageButtonText } from './helpers/webpageType';
|
||||
import { getWebpageButtonLangKey } from './helpers/webpageType';
|
||||
|
||||
import useDynamicColorListener from '../../../hooks/stickers/useDynamicColorListener';
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
import useEnsureStory from '../../../hooks/useEnsureStory';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
@ -23,6 +26,7 @@ import Audio from '../../common/Audio';
|
||||
import Document from '../../common/Document';
|
||||
import EmojiIconBackground from '../../common/embedded/EmojiIconBackground';
|
||||
import PeerColorWrapper from '../../common/PeerColorWrapper';
|
||||
import RadialPatternBackground from '../../common/profile/RadialPatternBackground';
|
||||
import SafeLink from '../../common/SafeLink';
|
||||
import StickerView from '../../common/StickerView';
|
||||
import Button from '../../ui/Button';
|
||||
@ -34,6 +38,7 @@ import './WebPage.scss';
|
||||
|
||||
const MAX_TEXT_LENGTH = 170; // symbols
|
||||
const WEBPAGE_STORY_TYPE = 'telegram_story';
|
||||
const WEBPAGE_GIFT_TYPE = 'telegram_nft';
|
||||
const STICKER_SIZE = 80;
|
||||
const EMOJI_SIZE = 38;
|
||||
|
||||
@ -60,8 +65,12 @@ type OwnProps = {
|
||||
onContainerClick?: ((e: React.MouseEvent) => void);
|
||||
isEditing?: boolean;
|
||||
};
|
||||
type StateProps = {
|
||||
canPlayAnimatedEmojis: boolean;
|
||||
};
|
||||
const STAR_GIFT_STICKER_SIZE = 120;
|
||||
|
||||
const WebPage: FC<OwnProps> = ({
|
||||
const WebPage: FC<OwnProps & StateProps> = ({
|
||||
message,
|
||||
observeIntersectionForLoading,
|
||||
observeIntersectionForPlaying,
|
||||
@ -89,8 +98,11 @@ const WebPage: FC<OwnProps> = ({
|
||||
const { isMobile } = useAppLayout();
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const stickersRef = useRef<HTMLDivElement>(null);
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const giftStickersRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const lang = useOldLang();
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
|
||||
const handleMediaClick = useLastCallback(() => {
|
||||
onMediaClick!();
|
||||
@ -100,8 +112,9 @@ const WebPage: FC<OwnProps> = ({
|
||||
onContainerClick?.(e);
|
||||
});
|
||||
|
||||
const handleQuickButtonClick = useLastCallback(() => {
|
||||
const handleOpenTelegramLink = useLastCallback(() => {
|
||||
if (!webPage) return;
|
||||
|
||||
openTelegramLink({
|
||||
url: webPage.url,
|
||||
});
|
||||
@ -132,8 +145,10 @@ const WebPage: FC<OwnProps> = ({
|
||||
mediaSize,
|
||||
} = webPage;
|
||||
const isStory = type === WEBPAGE_STORY_TYPE;
|
||||
const isGift = type === WEBPAGE_GIFT_TYPE;
|
||||
const isExpiredStory = story && 'isDeleted' in story;
|
||||
const quickButtonLangKey = !inPreview && !isExpiredStory ? getWebpageButtonText(type) : undefined;
|
||||
const quickButtonLangKey = !inPreview && !isExpiredStory ? getWebpageButtonLangKey(type) : undefined;
|
||||
const quickButtonTitle = quickButtonLangKey && lang(quickButtonLangKey);
|
||||
const truncatedDescription = trimText(description, MAX_TEXT_LENGTH);
|
||||
const isArticle = Boolean(truncatedDescription || title || siteName);
|
||||
let isSquarePhoto = Boolean(stickers);
|
||||
@ -159,31 +174,75 @@ const WebPage: FC<OwnProps> = ({
|
||||
video && 'with-video',
|
||||
!isArticle && 'no-article',
|
||||
document && 'with-document',
|
||||
quickButtonLangKey && 'with-quick-button',
|
||||
quickButtonTitle && 'with-quick-button',
|
||||
isGift && 'with-gift',
|
||||
);
|
||||
|
||||
function renderQuickButton(langKey: string) {
|
||||
function renderQuickButton(caption: string) {
|
||||
return (
|
||||
<Button
|
||||
className="WebPage--quick-button"
|
||||
size="tiny"
|
||||
color="translucent"
|
||||
isRectangular
|
||||
onClick={handleQuickButtonClick}
|
||||
onClick={handleOpenTelegramLink}
|
||||
>
|
||||
{lang(langKey)}
|
||||
{caption}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function renderStarGiftUnique() {
|
||||
const gift = webPage?.gift;
|
||||
if (!gift || gift.type !== 'starGiftUnique') return undefined;
|
||||
|
||||
const sticker = getStickerFromGift(gift)!;
|
||||
const attributes = getGiftAttributes(gift);
|
||||
const { backdrop, pattern, model } = attributes || {};
|
||||
|
||||
if (!backdrop || !pattern || !model) return undefined;
|
||||
|
||||
const backgroundColors = [backdrop.centerColor, backdrop.edgeColor];
|
||||
|
||||
return (
|
||||
<div
|
||||
className="web-page-gift web-page-centered web-page-unique"
|
||||
onClick={() => handleOpenTelegramLink()}
|
||||
>
|
||||
<div className="web-page-unique-background-wrapper">
|
||||
<RadialPatternBackground
|
||||
className="web-page-unique-background"
|
||||
backgroundColors={backgroundColors}
|
||||
patternColor={backdrop.patternColor}
|
||||
patternIcon={pattern.sticker}
|
||||
/>
|
||||
</div>
|
||||
<div ref={giftStickersRef} key={sticker.id} className="WebPage--unique-sticker">
|
||||
<StickerView
|
||||
containerRef={giftStickersRef}
|
||||
sticker={sticker}
|
||||
size={STAR_GIFT_STICKER_SIZE}
|
||||
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
||||
observeIntersectionForLoading={observeIntersectionForLoading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PeerColorWrapper
|
||||
className={className}
|
||||
data-initial={(siteName || displayUrl)[0]}
|
||||
dir={lang.isRtl ? 'rtl' : 'auto'}
|
||||
dir={oldLang.isRtl ? 'rtl' : 'auto'}
|
||||
onClick={handleContainerClick}
|
||||
>
|
||||
<div className={buildClassName('WebPage--content', isStory && 'is-story')}>
|
||||
<div className={buildClassName(
|
||||
'WebPage--content',
|
||||
isStory && 'is-story',
|
||||
isGift && 'is-gift',
|
||||
)}
|
||||
>
|
||||
{backgroundEmojiId && (
|
||||
<EmojiIconBackground
|
||||
emojiDocumentId={backgroundEmojiId}
|
||||
@ -193,21 +252,24 @@ const WebPage: FC<OwnProps> = ({
|
||||
{isStory && (
|
||||
<BaseStory story={story} isProtected={isProtected} isConnected={isConnected} isPreview />
|
||||
)}
|
||||
{isGift && !inPreview && (
|
||||
renderStarGiftUnique()
|
||||
)}
|
||||
{isArticle && (
|
||||
<div
|
||||
className={buildClassName('WebPage-text', !inPreview && 'WebPage-text_interactive')}
|
||||
onClick={!inPreview ? () => openUrl({ url, shouldSkipModal: true }) : undefined}
|
||||
>
|
||||
<SafeLink className="site-name" url={url} text={siteName || displayUrl} />
|
||||
{!inPreview && title && (
|
||||
{(!inPreview || isGift) && title && (
|
||||
<p className="site-title">{renderText(title)}</p>
|
||||
)}
|
||||
{truncatedDescription && (
|
||||
{truncatedDescription && !isGift && (
|
||||
<p className="site-description">{renderText(truncatedDescription, ['emoji', 'br'])}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{photo && !video && !document && (
|
||||
{photo && !isGift && !video && !document && (
|
||||
<Photo
|
||||
photo={photo}
|
||||
isOwn={message.isOutgoing}
|
||||
@ -289,13 +351,19 @@ const WebPage: FC<OwnProps> = ({
|
||||
{inPreview && displayUrl && !isArticle && (
|
||||
<div className="WebPage-text">
|
||||
<p className="site-name">{displayUrl}</p>
|
||||
<p className="site-description">{lang('Chat.Empty.LinkPreview')}</p>
|
||||
<p className="site-description">{oldLang('Chat.Empty.LinkPreview')}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{quickButtonLangKey && renderQuickButton(quickButtonLangKey)}
|
||||
{quickButtonTitle && renderQuickButton(quickButtonTitle)}
|
||||
</PeerColorWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(WebPage);
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
return {
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
};
|
||||
},
|
||||
)(WebPage));
|
||||
|
||||
@ -21,6 +21,10 @@
|
||||
}
|
||||
|
||||
.message-content {
|
||||
&.gift {
|
||||
--max-width: 18rem;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
max-width: var(--max-width);
|
||||
|
||||
|
||||
@ -138,6 +138,10 @@ export function buildContentClassName(
|
||||
if (webPage.document) {
|
||||
classNames.push('document');
|
||||
}
|
||||
|
||||
if (webPage.gift) {
|
||||
classNames.push('gift');
|
||||
}
|
||||
}
|
||||
|
||||
if (invoice && !invoice.extendedMedia) {
|
||||
|
||||
@ -1,36 +1,38 @@
|
||||
// https://github.com/telegramdesktop/tdesktop/blob/3da787791f6d227f69b32bf4003bc6071d05e2ac/Telegram/SourceFiles/history/view/history_view_view_button.cpp#L51
|
||||
export function getWebpageButtonText(type?: string) {
|
||||
export function getWebpageButtonLangKey(type?: string) {
|
||||
switch (type) {
|
||||
case 'telegram_channel_request':
|
||||
case 'telegram_megagroup_request':
|
||||
case 'telegram_chat_request':
|
||||
return 'lng_view_button_request_join';
|
||||
return 'ViewButtonRequestJoin';
|
||||
case 'telegram_message':
|
||||
return 'lng_view_button_message';
|
||||
return 'ViewButtonMessage';
|
||||
case 'telegram_bot':
|
||||
return 'lng_view_button_bot';
|
||||
return 'ViewButtonBot';
|
||||
case 'telegram_voicechat':
|
||||
return 'lng_view_button_voice_chat';
|
||||
return 'ViewButtonVoiceChat';
|
||||
case 'telegram_livestream':
|
||||
return 'lng_view_button_voice_chat_channel';
|
||||
return 'ViewButtonVoiceChatChannel';
|
||||
case 'telegram_megagroup':
|
||||
case 'telegram_chat':
|
||||
return 'lng_view_button_group';
|
||||
return 'ViewButtonGroup';
|
||||
case 'telegram_channel':
|
||||
return 'lng_view_button_channel';
|
||||
return 'ViewButtonChannel';
|
||||
case 'telegram_user':
|
||||
return 'lng_view_button_user';
|
||||
return 'ViewButtonUser';
|
||||
case 'telegram_botapp':
|
||||
return 'lng_view_button_bot_app';
|
||||
return 'ViewButtonBotApp';
|
||||
case 'telegram_chatlist':
|
||||
return 'ViewChatList';
|
||||
case 'telegram_story':
|
||||
return 'lng_view_button_story';
|
||||
return 'ViewButtonStory';
|
||||
case 'telegram_channel_boost':
|
||||
case 'telegram_group_boost':
|
||||
return 'lng_view_button_boost';
|
||||
return 'ViewButtonBoost';
|
||||
case 'telegram_stickerset':
|
||||
return 'lng_view_button_stickerset';
|
||||
return 'ViewButtonStickerset';
|
||||
case 'telegram_nft':
|
||||
return 'ViewButtonGiftUnique';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -312,11 +312,10 @@ const GiftInfoModal = ({
|
||||
const {
|
||||
model, backdrop, pattern, originalDetails,
|
||||
} = giftAttributes || {};
|
||||
const ownerId = gift.ownerId;
|
||||
const ownerName = gift.ownerName;
|
||||
tableData.push([
|
||||
lang('GiftInfoOwner'),
|
||||
ownerId ? { chatId: ownerId } : ownerName || '',
|
||||
gift.ownerId ? { chatId: gift.ownerId } : ownerName || '',
|
||||
]);
|
||||
|
||||
if (model) {
|
||||
|
||||
@ -990,3 +990,23 @@ addActionHandler('launchPrepaidStarsGiveaway', async (global, actions, payload):
|
||||
|
||||
actions.openBoostStatistics({ chatId, tabId });
|
||||
});
|
||||
|
||||
addActionHandler('openUniqueGiftBySlug', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
slug, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
const gift = await callApi('fetchUniqueStarGift', { slug });
|
||||
|
||||
if (!gift) {
|
||||
actions.showNotification({
|
||||
message: {
|
||||
key: 'GiftWasNotFound',
|
||||
},
|
||||
tabId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
actions.openGiftInfoModal({ gift, tabId });
|
||||
});
|
||||
|
||||
@ -1468,6 +1468,9 @@ export interface ActionPayloads {
|
||||
storyId: number;
|
||||
origin?: StoryViewerOrigin;
|
||||
} & WithTabId;
|
||||
openUniqueGiftBySlug: {
|
||||
slug: string;
|
||||
} & WithTabId;
|
||||
openPreviousStory: WithTabId | undefined;
|
||||
openNextStory: WithTabId | undefined;
|
||||
setStoryViewerMuted: {
|
||||
|
||||
@ -1713,6 +1713,7 @@ payments.getStarGifts#c4563590 hash:int = payments.StarGifts;
|
||||
payments.getUserStarGifts#5e72c7e1 user_id:InputUser offset:string limit:int = payments.UserStarGifts;
|
||||
payments.saveStarGift#92fd2aae flags:# unsave:flags.0?true msg_id:int = Bool;
|
||||
payments.convertStarGift#72770c83 msg_id:int = Bool;
|
||||
payments.getUniqueStarGift#a1974d72 slug:string = payments.UniqueStarGift;
|
||||
phone.requestCall#a6c4600c flags:# video:flags.0?true user_id:InputUser conference_call:flags.1?InputGroupCall random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
|
||||
@ -384,5 +384,6 @@
|
||||
"payments.getUserStarGifts",
|
||||
"payments.saveStarGift",
|
||||
"payments.convertStarGift",
|
||||
"payments.getUniqueStarGift",
|
||||
"fragment.getCollectibleInfo"
|
||||
]
|
||||
|
||||
@ -2508,6 +2508,7 @@ payments.getStarGifts#c4563590 hash:int = payments.StarGifts;
|
||||
payments.getUserStarGifts#5e72c7e1 user_id:InputUser offset:string limit:int = payments.UserStarGifts;
|
||||
payments.saveStarGift#92fd2aae flags:# unsave:flags.0?true msg_id:int = Bool;
|
||||
payments.convertStarGift#72770c83 msg_id:int = Bool;
|
||||
payments.getUniqueStarGift#a1974d72 slug:string = payments.UniqueStarGift;
|
||||
payments.botCancelStarsSubscription#6dfa0622 flags:# restore:flags.0?true user_id:InputUser charge_id:string = Bool;
|
||||
payments.getConnectedStarRefBots#5869a553 flags:# peer:InputPeer offset_date:flags.2?int offset_link:flags.2?string limit:int = payments.ConnectedStarRefBots;
|
||||
payments.getConnectedStarRefBot#b7d998f0 peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots;
|
||||
|
||||
15
src/types/language.d.ts
vendored
15
src/types/language.d.ts
vendored
@ -1222,6 +1222,21 @@ export interface LangPair {
|
||||
'ProfileTabSimilarChannels': undefined;
|
||||
'ActionUnsupportedTitle': undefined;
|
||||
'ActionUnsupportedDescription': undefined;
|
||||
'GiftWasNotFound': undefined;
|
||||
'ViewButtonRequestJoin': undefined;
|
||||
'ViewButtonMessage': undefined;
|
||||
'ViewButtonBot': undefined;
|
||||
'ViewButtonVoiceChat': undefined;
|
||||
'ViewButtonVoiceChatChannel': undefined;
|
||||
'ViewButtonGroup': undefined;
|
||||
'ViewButtonChannel': undefined;
|
||||
'ViewButtonUser': undefined;
|
||||
'ViewButtonBotApp': undefined;
|
||||
'ViewChatList': undefined;
|
||||
'ViewButtonStory': undefined;
|
||||
'ViewButtonBoost': undefined;
|
||||
'ViewButtonStickerset': undefined;
|
||||
'ViewButtonGiftUnique': undefined;
|
||||
}
|
||||
|
||||
export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
||||
|
||||
@ -8,7 +8,8 @@ import { IS_BAD_URL_PARSER } from './windowEnvironment';
|
||||
|
||||
export type DeepLinkMethod = 'resolve' | 'login' | 'passport' | 'settings' | 'join' | 'addstickers' | 'addemoji' |
|
||||
'setlanguage' | 'addtheme' | 'confirmphone' | 'socks' | 'proxy' | 'privatepost' | 'bg' | 'share' | 'msg' | 'msg_url' |
|
||||
'invoice' | 'addlist' | 'boost' | 'giftcode' | 'message' | 'premium_offer' | 'premium_multigift' | 'stars_topup';
|
||||
'invoice' | 'addlist' | 'boost' | 'giftcode' | 'message' | 'premium_offer' | 'premium_multigift' | 'stars_topup'
|
||||
| 'nft';
|
||||
|
||||
interface PublicMessageLink {
|
||||
type: 'publicMessageLink';
|
||||
@ -96,6 +97,11 @@ interface PremiumMultigiftLink {
|
||||
referrer: string;
|
||||
}
|
||||
|
||||
interface GiftUniqueLink {
|
||||
type: 'giftUniqueLink';
|
||||
slug: string;
|
||||
}
|
||||
|
||||
type DeepLink =
|
||||
TelegramPassportLink |
|
||||
LoginCodeLink |
|
||||
@ -108,7 +114,8 @@ type DeepLink =
|
||||
BusinessChatLink |
|
||||
PremiumReferrerLink |
|
||||
PremiumMultigiftLink |
|
||||
ChatBoostLink;
|
||||
ChatBoostLink |
|
||||
GiftUniqueLink;
|
||||
|
||||
type BuilderParams<T extends DeepLink> = Record<keyof Omit<T, 'type'>, string | undefined>;
|
||||
type BuilderReturnType<T extends DeepLink> = T | undefined;
|
||||
@ -229,6 +236,8 @@ function parseTgLink(url: URL) {
|
||||
return buildPremiumMultigiftLink({ referrer: queryParams.ref });
|
||||
case 'chatBoostLink':
|
||||
return buildChatBoostLink({ username: queryParams.domain, id: queryParams.channel });
|
||||
case 'giftUniqueLink':
|
||||
return buildGiftUniqueLink({ slug: queryParams.slug });
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -331,6 +340,12 @@ function parseHttpLink(url: URL) {
|
||||
id: isPrivateChannel ? pathParams[1] : undefined,
|
||||
});
|
||||
}
|
||||
case 'giftUniqueLink': {
|
||||
const slug = pathParams.slice(1).join('/');
|
||||
return buildGiftUniqueLink({
|
||||
slug,
|
||||
});
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -355,6 +370,7 @@ function getHttpDeepLinkType(
|
||||
if (method === 'login') return 'loginCodeLink';
|
||||
if (method === 'm') return 'businessChatLink';
|
||||
if (method === 'boost') return 'chatBoostLink';
|
||||
if (method === 'nft') return 'giftUniqueLink';
|
||||
if (method === 'c') {
|
||||
if (queryParams.boost !== undefined) return 'chatBoostLink';
|
||||
return 'privateChannelLink';
|
||||
@ -370,6 +386,7 @@ function getHttpDeepLinkType(
|
||||
if (isUsernameValid(pathParams[0]) && pathParams.slice(1).every(isNumber)) {
|
||||
return 'publicMessageLink';
|
||||
}
|
||||
if (method === 'nft') return 'giftUniqueLink';
|
||||
} else if (len === 4) {
|
||||
if (method === 'c' && pathParams.slice(1).every(isNumber)) {
|
||||
return 'privateMessageLink';
|
||||
@ -424,6 +441,8 @@ function getTgDeepLinkType(
|
||||
return 'premiumMultigiftLink';
|
||||
case 'boost':
|
||||
return 'chatBoostLink';
|
||||
case 'nft':
|
||||
return 'giftUniqueLink';
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -629,6 +648,21 @@ function buildBusinessChatLink(params: BuilderParams<BusinessChatLink>): Builder
|
||||
};
|
||||
}
|
||||
|
||||
function buildGiftUniqueLink(params: BuilderParams<GiftUniqueLink>): BuilderReturnType<GiftUniqueLink> {
|
||||
const {
|
||||
slug,
|
||||
} = params;
|
||||
|
||||
if (!slug) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'giftUniqueLink',
|
||||
slug,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPremiumReferrerLink(params: BuilderParams<PremiumReferrerLink>): BuilderReturnType<PremiumReferrerLink> {
|
||||
const {
|
||||
referrer,
|
||||
|
||||
@ -66,6 +66,9 @@ export const processDeepLink = (url: string): boolean => {
|
||||
isPrivate: Boolean(parsedLink.id),
|
||||
});
|
||||
return true;
|
||||
case 'giftUniqueLink':
|
||||
actions.openUniqueGiftBySlug({ slug: parsedLink.slug });
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user