Profile: Show collectible info (#4505)
This commit is contained in:
parent
37ad59d937
commit
1f834d42ed
@ -336,7 +336,7 @@ function buildAction(
|
||||
let currency: string | undefined;
|
||||
let giftCryptoInfo: {
|
||||
currency: string;
|
||||
amount: string;
|
||||
amount: number;
|
||||
} | undefined;
|
||||
let text: string;
|
||||
const translationValues: string[] = [];
|
||||
@ -499,10 +499,9 @@ function buildAction(
|
||||
}
|
||||
currency = action.currency;
|
||||
if (action.cryptoCurrency) {
|
||||
const cryptoAmountWithDecimals = action.cryptoAmount!.divide(1e7).toJSNumber() / 100;
|
||||
giftCryptoInfo = {
|
||||
currency: action.cryptoCurrency,
|
||||
amount: cryptoAmountWithDecimals.toFixed(2),
|
||||
amount: action.cryptoAmount!.toJSNumber(),
|
||||
};
|
||||
}
|
||||
amount = action.amount.toJSNumber();
|
||||
@ -552,10 +551,9 @@ function buildAction(
|
||||
}
|
||||
currency = action.currency;
|
||||
if (action.cryptoCurrency) {
|
||||
const cryptoAmountWithDecimals = action.cryptoAmount!.divide(1e7).toJSNumber() / 100;
|
||||
giftCryptoInfo = {
|
||||
currency: action.cryptoCurrency,
|
||||
amount: cryptoAmountWithDecimals.toFixed(2),
|
||||
amount: action.cryptoAmount!.toJSNumber(),
|
||||
};
|
||||
}
|
||||
if (action.boostPeer) {
|
||||
|
||||
@ -3,6 +3,7 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
||||
import type { ApiPrivacyKey } from '../../../types';
|
||||
import type {
|
||||
ApiChatLink,
|
||||
ApiCollectionInfo,
|
||||
ApiConfig, ApiCountry, ApiLangString,
|
||||
ApiPeerColors,
|
||||
ApiSession, ApiTimezone, ApiUrlAuthResult, ApiWallpaper, ApiWebSession,
|
||||
@ -274,3 +275,23 @@ export function buildApiChatLink(data: GramJs.account.ResolvedBusinessChatLinks)
|
||||
text: buildMessageTextContent(data.message, data.entities),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiCollectibleInfo(info: GramJs.fragment.TypeCollectibleInfo): ApiCollectionInfo {
|
||||
const {
|
||||
amount,
|
||||
currency,
|
||||
cryptoAmount,
|
||||
cryptoCurrency,
|
||||
purchaseDate,
|
||||
url,
|
||||
} = info;
|
||||
|
||||
return {
|
||||
amount: amount.toJSNumber(),
|
||||
currency,
|
||||
cryptoAmount: cryptoAmount.toJSNumber(),
|
||||
cryptoCurrency,
|
||||
purchaseDate,
|
||||
url,
|
||||
};
|
||||
}
|
||||
|
||||
26
src/api/gramjs/methods/fragment.ts
Normal file
26
src/api/gramjs/methods/fragment.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import { buildApiCollectibleInfo } from '../apiBuilders/misc';
|
||||
import { invokeRequest } from './client';
|
||||
|
||||
type InputCollectible = {
|
||||
phone: string;
|
||||
} | {
|
||||
username: string;
|
||||
};
|
||||
|
||||
export async function fetchCollectionInfo(collectible: InputCollectible) {
|
||||
const inputCollectible = 'username' in collectible
|
||||
? new GramJs.InputCollectibleUsername({ username: collectible.username })
|
||||
: new GramJs.InputCollectiblePhone({ phone: collectible.phone });
|
||||
|
||||
const result = await invokeRequest(new GramJs.fragment.GetCollectibleInfo({
|
||||
collectible: inputCollectible,
|
||||
}));
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return buildApiCollectibleInfo(result);
|
||||
}
|
||||
@ -102,3 +102,5 @@ export {
|
||||
applyBoost, fetchBoostList, fetchBoostStatus, fetchGiveawayInfo, fetchMyBoosts, applyGiftCode, checkGiftCode,
|
||||
getPremiumGiftCodeOptions, launchPrepaidGiveaway,
|
||||
} from './payments';
|
||||
|
||||
export * from './fragment';
|
||||
|
||||
@ -351,7 +351,7 @@ export interface ApiAction {
|
||||
currency?: string;
|
||||
giftCryptoInfo?: {
|
||||
currency: string;
|
||||
amount: string;
|
||||
amount: number;
|
||||
};
|
||||
translationValues: string[];
|
||||
call?: Partial<ApiGroupCall>;
|
||||
|
||||
@ -267,3 +267,12 @@ type ApiUrlAuthResultDefault = {
|
||||
};
|
||||
|
||||
export type ApiUrlAuthResult = ApiUrlAuthResultRequest | ApiUrlAuthResultAccepted | ApiUrlAuthResultDefault;
|
||||
|
||||
export interface ApiCollectionInfo {
|
||||
amount: number;
|
||||
currency: string;
|
||||
cryptoAmount: number;
|
||||
cryptoCurrency: string;
|
||||
purchaseDate: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
BIN
src/assets/tgs/general/Fragment.tgs
Normal file
BIN
src/assets/tgs/general/Fragment.tgs
Normal file
Binary file not shown.
BIN
src/assets/tgs/general/Mention.tgs
Normal file
BIN
src/assets/tgs/general/Mention.tgs
Normal file
Binary file not shown.
@ -39,6 +39,7 @@ export { default as CountryPickerModal } from '../components/common/CountryPicke
|
||||
export { default as ReactorListModal } from '../components/middle/ReactorListModal';
|
||||
export { default as EmojiInteractionAnimation } from '../components/middle/EmojiInteractionAnimation';
|
||||
export { default as ChatLanguageModal } from '../components/middle/ChatLanguageModal';
|
||||
export { default as CollectibleInfoModal } from '../components/modals/collectible/CollectibleInfoModal';
|
||||
|
||||
export { default as LeftSearch } from '../components/left/search/LeftSearch';
|
||||
export { default as Settings } from '../components/left/settings/Settings';
|
||||
|
||||
@ -8,6 +8,8 @@ import VoiceMini from '../../../assets/tgs/calls/VoiceMini.tgs';
|
||||
import VoiceMuted from '../../../assets/tgs/calls/VoiceMuted.tgs';
|
||||
import VoiceOutlined from '../../../assets/tgs/calls/VoiceOutlined.tgs';
|
||||
import Flame from '../../../assets/tgs/general/Flame.tgs';
|
||||
import Fragment from '../../../assets/tgs/general/Fragment.tgs';
|
||||
import Mention from '../../../assets/tgs/general/Mention.tgs';
|
||||
import PartyPopper from '../../../assets/tgs/general/PartyPopper.tgs';
|
||||
import Invite from '../../../assets/tgs/invites/Invite.tgs';
|
||||
import JoinRequest from '../../../assets/tgs/invites/Requests.tgs';
|
||||
@ -54,4 +56,6 @@ export const LOCAL_TGS_URLS = {
|
||||
ReadTime,
|
||||
Unlock,
|
||||
LastSeen,
|
||||
Mention,
|
||||
Fragment,
|
||||
};
|
||||
|
||||
5
src/components/common/helpers/formatUsername.ts
Normal file
5
src/components/common/helpers/formatUsername.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { TME_LINK_PREFIX } from '../../../config';
|
||||
|
||||
export default function formatUsername(username: string, asAbsoluteLink?: boolean) {
|
||||
return asAbsoluteLink ? `${TME_LINK_PREFIX}${username}` : `@${username}`;
|
||||
}
|
||||
@ -138,7 +138,8 @@ export function renderActionMessageText(
|
||||
let priceText = price;
|
||||
|
||||
if (giftCryptoInfo) {
|
||||
priceText = `${giftCryptoInfo.amount} ${giftCryptoInfo.currency} (~${price})`;
|
||||
const cryptoPrice = formatCurrency(giftCryptoInfo.amount, giftCryptoInfo.currency, lang.code);
|
||||
priceText = `${cryptoPrice} (${price})`;
|
||||
}
|
||||
|
||||
processed = processPlaceholder(
|
||||
|
||||
@ -9,7 +9,7 @@ import type {
|
||||
} from '../../../api/types';
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
|
||||
import { TME_LINK_PREFIX } from '../../../config';
|
||||
import { FRAGMENT_PHONE_CODE, FRAGMENT_PHONE_LENGTH } from '../../../config';
|
||||
import {
|
||||
buildStaticMapHash,
|
||||
getChatLink,
|
||||
@ -33,6 +33,7 @@ import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
||||
import { debounce } from '../../../util/schedulers';
|
||||
import stopEvent from '../../../util/stopEvent';
|
||||
import { ChatAnimationTypes } from '../../left/main/hooks';
|
||||
import formatUsername from '../helpers/formatUsername';
|
||||
import renderText from '../helpers/renderText';
|
||||
|
||||
import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps';
|
||||
@ -102,6 +103,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
loadPeerStories,
|
||||
openSavedDialog,
|
||||
openMapModal,
|
||||
requestCollectibleInfo,
|
||||
} = getActions();
|
||||
|
||||
const {
|
||||
@ -206,10 +208,6 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
openSavedDialog({ chatId: chatOrUserId });
|
||||
});
|
||||
|
||||
if (!chat || chat.isRestricted || (isSelf && !isInSettings)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function copy(text: string, entity: string) {
|
||||
copyTextToClipboard(text);
|
||||
showNotification({ message: `${entity} was copied` });
|
||||
@ -217,6 +215,26 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const formattedNumber = phoneNumber && formatPhoneNumberWithCode(phoneCodeList, phoneNumber);
|
||||
|
||||
const handlePhoneClick = useLastCallback(() => {
|
||||
if (phoneNumber?.length === FRAGMENT_PHONE_LENGTH && phoneNumber.startsWith(FRAGMENT_PHONE_CODE)) {
|
||||
requestCollectibleInfo({ collectible: phoneNumber, userId: userId!, type: 'phone' });
|
||||
return;
|
||||
}
|
||||
copy(formattedNumber!, lang('Phone'));
|
||||
});
|
||||
|
||||
const handleUsernameClick = useLastCallback((username: ApiUsername, isChat?: boolean) => {
|
||||
if (!username.isEditable) {
|
||||
requestCollectibleInfo({ collectible: username.username, userId: userId!, type: 'username' });
|
||||
return;
|
||||
}
|
||||
copy(formatUsername(username.username, isChat), lang(isChat ? 'Link' : 'Username'));
|
||||
});
|
||||
|
||||
if (!chat || chat.isRestricted || (isSelf && !isInSettings)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function renderUsernames(usernameList: ApiUsername[], isChat?: boolean) {
|
||||
const [mainUsername, ...otherUsernames] = usernameList;
|
||||
|
||||
@ -226,21 +244,21 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
.map((s) => {
|
||||
return (s === 'USERNAMES' ? (
|
||||
<>
|
||||
{otherUsernames.map(({ username: nick }, idx) => {
|
||||
const textToCopy = isChat ? `${TME_LINK_PREFIX}${nick}` : `@${nick}`;
|
||||
{otherUsernames.map((username, idx) => {
|
||||
return (
|
||||
<>
|
||||
{idx > 0 ? ', ' : ''}
|
||||
<a
|
||||
key={nick}
|
||||
href={`${TME_LINK_PREFIX}${nick}`}
|
||||
key={username.username}
|
||||
href={formatUsername(username.username, true)}
|
||||
onMouseDown={stopEvent}
|
||||
onClick={(e) => {
|
||||
stopEvent(e);
|
||||
copy(textToCopy, lang(isChat ? 'Link' : 'Username'));
|
||||
handleUsernameClick(username, isChat);
|
||||
}}
|
||||
className="text-entity-link username-link"
|
||||
>
|
||||
{`@${nick}`}
|
||||
{formatUsername(username.username)}
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
@ -250,9 +268,6 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const username = isChat ? `t.me/${mainUsername.username}` : mainUsername.username;
|
||||
const textToCopy = isChat ? `${TME_LINK_PREFIX}${mainUsername.username}` : `@${mainUsername.username}`;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
icon={isChat ? 'link' : 'mention'}
|
||||
@ -260,9 +275,11 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
narrow
|
||||
ripple
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => copy(textToCopy, lang(isChat ? 'Link' : 'Username'))}
|
||||
onClick={() => {
|
||||
handleUsernameClick(mainUsername, isChat);
|
||||
}}
|
||||
>
|
||||
<span className="title" dir="auto">{username}</span>
|
||||
<span className="title" dir="auto">{formatUsername(mainUsername.username, isChat)}</span>
|
||||
<span className="subtitle">
|
||||
{usernameLinks && <span className="other-usernames">{usernameLinks}</span>}
|
||||
{lang(isChat ? 'Link' : 'Username')}
|
||||
@ -289,9 +306,9 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{formattedNumber && Boolean(formattedNumber.length) && (
|
||||
{Boolean(formattedNumber?.length) && (
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
<ListItem icon="phone" multiline narrow ripple onClick={() => copy(formattedNumber, lang('Phone'))}>
|
||||
<ListItem icon="phone" multiline narrow ripple onClick={handlePhoneClick}>
|
||||
<span className="title" dir="auto">{formattedNumber}</span>
|
||||
<span className="subtitle">{lang('Phone')}</span>
|
||||
</ListItem>
|
||||
|
||||
@ -9,6 +9,7 @@ import { pick } from '../../util/iteratees';
|
||||
import AttachBotInstallModal from './attachBotInstall/AttachBotInstallModal.async';
|
||||
import BoostModal from './boost/BoostModal.async';
|
||||
import ChatlistModal from './chatlist/ChatlistModal.async';
|
||||
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
|
||||
import GiftCodeModal from './giftcode/GiftCodeModal.async';
|
||||
import InviteViaLinkModal from './inviteViaLink/InviteViaLinkModal.async';
|
||||
import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
|
||||
@ -25,6 +26,7 @@ type ModalKey = keyof Pick<TabState,
|
||||
'oneTimeMediaModal' |
|
||||
'inviteViaLinkModal' |
|
||||
'requestedAttachBotInstall' |
|
||||
'collectibleInfoModal' |
|
||||
'reportAdModal' |
|
||||
'webApp'
|
||||
>;
|
||||
@ -51,6 +53,7 @@ const MODALS: ModalRegistry = {
|
||||
requestedAttachBotInstall: AttachBotInstallModal,
|
||||
reportAdModal: ReportAdModal,
|
||||
webApp: WebAppModal,
|
||||
collectibleInfoModal: CollectibleInfoModal,
|
||||
};
|
||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React from '../../../lib/teact/teact';
|
||||
|
||||
import type { OwnProps } from './CollectibleInfoModal';
|
||||
|
||||
import { Bundles } from '../../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||
|
||||
const CollectibleInfoModalAsync: FC<OwnProps> = (props) => {
|
||||
const { modal } = props;
|
||||
const CollectibleInfoModal = useModuleLoader(Bundles.Extra, 'CollectibleInfoModal', !modal);
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return CollectibleInfoModal ? <CollectibleInfoModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default CollectibleInfoModalAsync;
|
||||
@ -0,0 +1,33 @@
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
left: 0.5rem;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
border-radius: 50%;
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
flex-shrink: 0;
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.title, .description {
|
||||
text-align: center !important;
|
||||
text-wrap: pretty;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
156
src/components/modals/collectible/CollectibleInfoModal.tsx
Normal file
156
src/components/modals/collectible/CollectibleInfoModal.tsx
Normal file
@ -0,0 +1,156 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo,
|
||||
useMemo,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiCountryCode } from '../../../api/types';
|
||||
import type { TabState } from '../../../global/types';
|
||||
|
||||
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
import { formatDateAtTime } from '../../../util/date/dateFormat';
|
||||
import { formatCurrency } from '../../../util/formatCurrency';
|
||||
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
||||
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
||||
import formatUsername from '../../common/helpers/formatUsername';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||
import Icon from '../../common/Icon';
|
||||
import PickerSelectedItem from '../../common/PickerSelectedItem';
|
||||
import Button from '../../ui/Button';
|
||||
import Modal from '../../ui/Modal';
|
||||
|
||||
import styles from './CollectibleInfoModal.module.scss';
|
||||
|
||||
export type OwnProps = {
|
||||
modal: TabState['collectibleInfoModal'];
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
phoneCodeList: ApiCountryCode[];
|
||||
};
|
||||
|
||||
const TOP_ICON_SIZE = 60;
|
||||
|
||||
const CollectibleInfoModal: FC<OwnProps & StateProps> = ({
|
||||
modal,
|
||||
phoneCodeList,
|
||||
}) => {
|
||||
const {
|
||||
closeCollectibleInfoModal,
|
||||
openChat,
|
||||
openUrl,
|
||||
showNotification,
|
||||
} = getActions();
|
||||
const lang = useLang();
|
||||
|
||||
const isUsername = modal?.type === 'username';
|
||||
|
||||
const handleClose = useLastCallback(() => {
|
||||
closeCollectibleInfoModal();
|
||||
});
|
||||
|
||||
const handleOpenChat = useLastCallback(() => {
|
||||
openChat({ id: modal!.userId });
|
||||
handleClose();
|
||||
});
|
||||
|
||||
const handleOpenUrl = useLastCallback(() => {
|
||||
openUrl({
|
||||
url: modal!.url,
|
||||
shouldSkipModal: true,
|
||||
});
|
||||
handleClose();
|
||||
});
|
||||
|
||||
const handleCopy = useLastCallback(() => {
|
||||
const text = isUsername ? formatUsername(modal!.collectible)
|
||||
: formatPhoneNumberWithCode(phoneCodeList, modal!.collectible);
|
||||
copyTextToClipboard(text);
|
||||
showNotification({
|
||||
message: lang(isUsername ? 'UsernameCopied' : 'PhoneCopied'),
|
||||
});
|
||||
handleClose();
|
||||
});
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (!modal) return undefined;
|
||||
const key = isUsername ? 'FragmentUsernameTitle' : 'FragmentPhoneTitle';
|
||||
const formattedCollectible = isUsername
|
||||
? formatUsername(modal.collectible)
|
||||
: formatPhoneNumberWithCode(phoneCodeList, modal.collectible);
|
||||
return lang(key, formattedCollectible);
|
||||
}, [modal, isUsername, phoneCodeList, lang]);
|
||||
|
||||
const description = useMemo(() => {
|
||||
if (!modal) return undefined;
|
||||
const key = isUsername ? 'FragmentUsernameMessage' : 'FragmentPhoneMessage';
|
||||
const date = formatDateAtTime(lang, modal.purchaseDate * 1000);
|
||||
const currency = formatCurrency(modal.amount, modal.currency, lang.code);
|
||||
const cryptoCurrency = formatCurrency(modal.cryptoAmount, modal.cryptoCurrency, lang.code);
|
||||
const paid = `${cryptoCurrency} (${currency})`;
|
||||
return lang(key, [date, paid]);
|
||||
}, [modal, isUsername, lang]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={Boolean(modal)}
|
||||
isSlim
|
||||
contentClassName={styles.content}
|
||||
onClose={closeCollectibleInfoModal}
|
||||
>
|
||||
<Button
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
className={styles.closeButton}
|
||||
ariaLabel={lang('Close')}
|
||||
onClick={handleClose}
|
||||
>
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
<div className={styles.icon}>
|
||||
<AnimatedIconWithPreview
|
||||
tgsUrl={isUsername ? LOCAL_TGS_URLS.Mention : LOCAL_TGS_URLS.Fragment}
|
||||
size={TOP_ICON_SIZE}
|
||||
/>
|
||||
</div>
|
||||
<h3 className={styles.title}>
|
||||
{title && renderText(title, ['simple_markdown'])}
|
||||
</h3>
|
||||
<PickerSelectedItem
|
||||
fluid
|
||||
className={styles.chip}
|
||||
peerId={modal?.userId}
|
||||
forceShowSelf
|
||||
onClick={handleOpenChat}
|
||||
/>
|
||||
<p className={styles.description}>
|
||||
{description && renderText(description, ['simple_markdown'])}
|
||||
</p>
|
||||
<div className="dialog-buttons">
|
||||
<Button className="confirm-dialog-button" onClick={handleOpenUrl}>
|
||||
{lang('FragmentUsernameOpen')}
|
||||
</Button>
|
||||
<Button isText className="confirm-dialog-button" onClick={handleCopy}>
|
||||
{lang(isUsername ? 'FragmentUsernameCopy' : 'FragmentPhoneCopy')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const { countryList } = global;
|
||||
|
||||
return {
|
||||
phoneCodeList: countryList.phoneCodes,
|
||||
};
|
||||
},
|
||||
)(CollectibleInfoModal));
|
||||
@ -319,6 +319,8 @@ export const GIVEAWAY_MAX_ADDITIONAL_CHANNELS = 10;
|
||||
export const GIVEAWAY_MAX_ADDITIONAL_USERS = 10;
|
||||
export const GIVEAWAY_MAX_ADDITIONAL_COUNTRIES = 10;
|
||||
export const BOOST_PER_SENT_GIFT = 3;
|
||||
export const FRAGMENT_PHONE_CODE = '888';
|
||||
export const FRAGMENT_PHONE_LENGTH = 11;
|
||||
|
||||
export const LIGHT_THEME_BG_COLOR = '#99BA92';
|
||||
export const DARK_THEME_BG_COLOR = '#0F0F0F';
|
||||
|
||||
@ -30,6 +30,7 @@ import {
|
||||
TOPICS_SLICE,
|
||||
TOPICS_SLICE_SECOND_LOAD,
|
||||
} from '../../../config';
|
||||
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
import { formatShareText, parseChooseParameter, processDeepLink } from '../../../util/deeplink';
|
||||
import { isDeepLink } from '../../../util/deepLinkParser';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
@ -2673,6 +2674,38 @@ addActionHandler('resolveBusinessChatLink', async (global, actions, payload): Pr
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('requestCollectibleInfo', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
type, collectible, userId, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
let inputCollectible;
|
||||
if (type === 'phone') {
|
||||
inputCollectible = { phone: collectible };
|
||||
}
|
||||
if (type === 'username') {
|
||||
inputCollectible = { username: collectible };
|
||||
}
|
||||
if (!inputCollectible) return;
|
||||
|
||||
const result = await callApi('fetchCollectionInfo', inputCollectible);
|
||||
if (!result) {
|
||||
copyTextToClipboard(collectible);
|
||||
return;
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
global = updateTabState(global, {
|
||||
collectibleInfoModal: {
|
||||
...result,
|
||||
type,
|
||||
collectible,
|
||||
userId,
|
||||
},
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
async function loadChats(
|
||||
listType: ChatListType,
|
||||
offsetId?: string,
|
||||
|
||||
@ -744,6 +744,13 @@ addActionHandler('closeInviteViaLinkModal', (global, actions, payload): ActionRe
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('closeCollectibleInfoModal', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload ?? {};
|
||||
return updateTabState(global, {
|
||||
collectibleInfoModal: undefined,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('setShouldCloseRightColumn', (global, actions, payload): ActionReturnType => {
|
||||
const { value, tabId = getCurrentTabId() } = payload;
|
||||
return updateTabState(global, {
|
||||
|
||||
@ -16,6 +16,7 @@ import type {
|
||||
ApiChatReactions,
|
||||
ApiChatType,
|
||||
ApiCheckedGiftCode,
|
||||
ApiCollectionInfo,
|
||||
ApiConfig,
|
||||
ApiContact,
|
||||
ApiCountry,
|
||||
@ -738,6 +739,12 @@ export type TabState = {
|
||||
oneTimeMediaModal?: {
|
||||
message: ApiMessage;
|
||||
};
|
||||
|
||||
collectibleInfoModal?: ApiCollectionInfo & {
|
||||
userId: string;
|
||||
type: 'phone' | 'username';
|
||||
collectible: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type GlobalState = {
|
||||
@ -2867,6 +2874,13 @@ export interface ActionPayloads {
|
||||
openOneTimeMediaModal: TabState['oneTimeMediaModal'] & WithTabId;
|
||||
closeOneTimeMediaModal: WithTabId | undefined;
|
||||
|
||||
requestCollectibleInfo: {
|
||||
userId: string;
|
||||
type : 'phone' | 'username';
|
||||
collectible: string;
|
||||
} & WithTabId;
|
||||
closeCollectibleInfoModal: WithTabId | undefined;
|
||||
|
||||
// Calls
|
||||
joinGroupCall: {
|
||||
chatId?: string;
|
||||
|
||||
@ -1637,4 +1637,5 @@ stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
|
||||
premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList;
|
||||
premium.getMyBoosts#be77b4a = premium.MyBoosts;
|
||||
premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = premium.MyBoosts;
|
||||
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;`;
|
||||
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;
|
||||
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;`;
|
||||
@ -349,5 +349,6 @@
|
||||
"payments.applyGiftCode",
|
||||
"payments.getGiveawayInfo",
|
||||
"payments.getPremiumGiftCodeOptions",
|
||||
"payments.launchPrepaidGiveaway"
|
||||
"payments.launchPrepaidGiveaway",
|
||||
"fragment.getCollectibleInfo"
|
||||
]
|
||||
|
||||
@ -24,6 +24,9 @@ export function formatCurrency(
|
||||
}
|
||||
|
||||
function getCurrencyExp(currency: string) {
|
||||
if (currency === 'TON') {
|
||||
return 9;
|
||||
}
|
||||
if (currency === 'CLF') {
|
||||
return 4;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user