Emoji Status: Support EmojiStatusCollectible (#5547)
Co-authored-by: Alexander Zinchuk <alx.zinchuk@gmail.com>
This commit is contained in:
parent
79c904fe17
commit
3491a3b63b
@ -1,9 +1,10 @@
|
||||
import type BigInt from 'big-integer';
|
||||
import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import type { ApiEmojiStatus, ApiPeerColor } from '../../types';
|
||||
import type { ApiEmojiStatusType, ApiPeerColor } from '../../types';
|
||||
|
||||
import { CHANNEL_ID_LENGTH } from '../../../config';
|
||||
import { numberToHexColor } from '../../../util/colors';
|
||||
|
||||
export function isPeerUser(peer: GramJs.TypePeer | GramJs.TypeInputPeer): peer is GramJs.PeerUser {
|
||||
return peer.hasOwnProperty('userId');
|
||||
@ -50,14 +51,30 @@ export function buildApiPeerColor(peerColor: GramJs.TypePeerColor): ApiPeerColor
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiEmojiStatus(mtpEmojiStatus: GramJs.TypeEmojiStatus): ApiEmojiStatus | undefined {
|
||||
export function buildApiEmojiStatus(mtpEmojiStatus: GramJs.TypeEmojiStatus):
|
||||
ApiEmojiStatusType | undefined {
|
||||
if (mtpEmojiStatus instanceof GramJs.EmojiStatus) {
|
||||
return { documentId: mtpEmojiStatus.documentId.toString(), until: mtpEmojiStatus.until };
|
||||
return {
|
||||
type: 'regular',
|
||||
documentId: mtpEmojiStatus.documentId.toString(),
|
||||
until: mtpEmojiStatus.until,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Support other parameters
|
||||
if (mtpEmojiStatus instanceof GramJs.EmojiStatusCollectible) {
|
||||
return { documentId: mtpEmojiStatus.documentId.toString(), until: mtpEmojiStatus.until };
|
||||
return {
|
||||
type: 'collectible',
|
||||
collectibleId: mtpEmojiStatus.collectibleId.toString(),
|
||||
documentId: mtpEmojiStatus.documentId.toString(),
|
||||
title: mtpEmojiStatus.title,
|
||||
slug: mtpEmojiStatus.slug,
|
||||
patternDocumentId: mtpEmojiStatus.patternDocumentId.toString(),
|
||||
centerColor: numberToHexColor(mtpEmojiStatus.centerColor),
|
||||
edgeColor: numberToHexColor(mtpEmojiStatus.edgeColor),
|
||||
patternColor: numberToHexColor(mtpEmojiStatus.patternColor),
|
||||
textColor: numberToHexColor(mtpEmojiStatus.textColor),
|
||||
until: mtpEmojiStatus.until,
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@ -8,6 +8,7 @@ import type {
|
||||
ApiChatBannedRights,
|
||||
ApiChatFolder,
|
||||
ApiChatReactions,
|
||||
ApiEmojiStatusType,
|
||||
ApiFormattedText,
|
||||
ApiGroupCall,
|
||||
ApiInputPrivacyRules,
|
||||
@ -735,14 +736,21 @@ export function buildInputChatReactions(chatReactions?: ApiChatReactions) {
|
||||
return new GramJs.ChatReactionsNone();
|
||||
}
|
||||
|
||||
export function buildInputEmojiStatus(emojiStatusId: string, expires?: number) {
|
||||
if (emojiStatusId === DEFAULT_STATUS_ICON_ID) {
|
||||
export function buildInputEmojiStatus(emojiStatus: ApiEmojiStatusType) {
|
||||
if (emojiStatus.type === 'collectible') {
|
||||
return new GramJs.InputEmojiStatusCollectible({
|
||||
collectibleId: BigInt(emojiStatus.collectibleId),
|
||||
until: emojiStatus.until,
|
||||
});
|
||||
}
|
||||
|
||||
if (emojiStatus.documentId === DEFAULT_STATUS_ICON_ID) {
|
||||
return new GramJs.EmojiStatusEmpty();
|
||||
}
|
||||
|
||||
return new GramJs.EmojiStatus({
|
||||
documentId: BigInt(emojiStatusId),
|
||||
until: expires,
|
||||
documentId: BigInt(emojiStatus.documentId),
|
||||
until: emojiStatus.until,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -276,6 +276,23 @@ export async function fetchDefaultStatusEmojis() {
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchCollectibleEmojiStatuses({ hash = '0' }: { hash?: string }) {
|
||||
const result = await invokeRequest(new GramJs.account.GetCollectibleEmojiStatuses(
|
||||
{ hash: BigInt(hash) },
|
||||
));
|
||||
|
||||
if (!(result instanceof GramJs.account.EmojiStatuses)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const statuses = result.statuses.map(buildApiEmojiStatus).filter(Boolean);
|
||||
|
||||
return {
|
||||
statuses,
|
||||
hash: String(result.hash),
|
||||
};
|
||||
}
|
||||
|
||||
export async function searchStickers({ query, hash = '0' }: { query: string; hash?: string }) {
|
||||
const result = await invokeRequest(new GramJs.messages.SearchStickerSets({
|
||||
q: query,
|
||||
|
||||
@ -2,7 +2,7 @@ import BigInt from 'big-integer';
|
||||
import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import type {
|
||||
ApiChat, ApiPeer, ApiUser,
|
||||
ApiChat, ApiEmojiStatusType, ApiPeer, ApiUser,
|
||||
} from '../../types';
|
||||
|
||||
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||
@ -304,9 +304,9 @@ export function reportSpam(userOrChat: ApiPeer) {
|
||||
});
|
||||
}
|
||||
|
||||
export function updateEmojiStatus(emojiStatusId: string, expires?: number) {
|
||||
export function updateEmojiStatus(emojiStatus: ApiEmojiStatusType) {
|
||||
return invokeRequest(new GramJs.account.UpdateEmojiStatus({
|
||||
emojiStatus: buildInputEmojiStatus(emojiStatusId, expires),
|
||||
emojiStatus: buildInputEmojiStatus(emojiStatus),
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@ import type {
|
||||
} from './messages';
|
||||
import type { ApiBotVerification, ApiChatInviteImporter } from './misc';
|
||||
import type {
|
||||
ApiEmojiStatus, ApiFakeType, ApiUser, ApiUsername,
|
||||
ApiEmojiStatusType, ApiFakeType, ApiUser, ApiUsername,
|
||||
} from './users';
|
||||
|
||||
type ApiChatType = (
|
||||
@ -44,7 +44,7 @@ export interface ApiChat {
|
||||
isProtected?: boolean;
|
||||
fakeType?: ApiFakeType;
|
||||
color?: ApiPeerColor;
|
||||
emojiStatus?: ApiEmojiStatus;
|
||||
emojiStatus?: ApiEmojiStatusType;
|
||||
isForum?: boolean;
|
||||
isForumAsMessages?: true;
|
||||
boostLevel?: number;
|
||||
|
||||
@ -40,7 +40,7 @@ import type { ApiStarsAmount } from './payments';
|
||||
import type { ApiPrivacyKey, LangPackStringValue, PrivacyVisibility } from './settings';
|
||||
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
|
||||
import type {
|
||||
ApiEmojiStatus, ApiUser, ApiUserFullInfo, ApiUserStatus,
|
||||
ApiEmojiStatusType, ApiUser, ApiUserFullInfo, ApiUserStatus,
|
||||
} from './users';
|
||||
|
||||
export type ApiUpdateReady = {
|
||||
@ -419,7 +419,7 @@ export type ApiUpdateUserStatus = {
|
||||
export type ApiUpdateUserEmojiStatus = {
|
||||
'@type': 'updateUserEmojiStatus';
|
||||
userId: string;
|
||||
emojiStatus?: ApiEmojiStatus;
|
||||
emojiStatus?: ApiEmojiStatusType;
|
||||
};
|
||||
|
||||
export type ApiUpdateRecentEmojiStatuses = {
|
||||
|
||||
@ -28,7 +28,7 @@ export interface ApiUser {
|
||||
canBeInvitedToGroup?: boolean;
|
||||
fakeType?: ApiFakeType;
|
||||
isAttachBot?: boolean;
|
||||
emojiStatus?: ApiEmojiStatus;
|
||||
emojiStatus?: ApiEmojiStatusType;
|
||||
areStoriesHidden?: boolean;
|
||||
hasStories?: boolean;
|
||||
hasUnreadStories?: boolean;
|
||||
@ -132,11 +132,28 @@ export interface ApiPremiumGiftOption {
|
||||
botUrl: string;
|
||||
}
|
||||
|
||||
export type ApiEmojiStatusType = ApiEmojiStatus | ApiEmojiStatusCollectible;
|
||||
|
||||
export interface ApiEmojiStatus {
|
||||
type: 'regular';
|
||||
documentId: string;
|
||||
until?: number;
|
||||
}
|
||||
|
||||
export interface ApiEmojiStatusCollectible {
|
||||
type: 'collectible';
|
||||
collectibleId: string;
|
||||
documentId: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
patternDocumentId: string;
|
||||
centerColor: string;
|
||||
edgeColor: string;
|
||||
patternColor: string;
|
||||
textColor: string;
|
||||
until?: number;
|
||||
}
|
||||
|
||||
export interface ApiBirthday {
|
||||
day: number;
|
||||
month: number;
|
||||
|
||||
1
src/assets/font-icons/crown-take-off.svg
Normal file
1
src/assets/font-icons/crown-take-off.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" fill-rule="evenodd" d="M2.17 1.348a1.206 1.206 0 0 0-1.694 0 1.18 1.18 0 0 0 0 1.68L29.35 31.652a1.206 1.206 0 0 0 1.694 0 1.18 1.18 0 0 0 0-1.68l-4.523-4.484q.072-.228.12-.466l.23-1.146h-1.976l-2.529-2.508h5.012l2.04-10.11A3.03 3.03 0 0 0 32 8.27c0-1.669-1.364-3.022-3.047-3.022s-3.048 1.353-3.048 3.022c0 .812.324 1.55.85 2.093l-4.66 5.46-4.66-8.664a3.02 3.02 0 0 0 1.613-2.666c0-1.669-1.365-3.022-3.048-3.022s-3.048 1.353-3.048 3.022c0 1.154.654 2.158 1.614 2.666l-2.272 4.224zm7.818 14.321-1.4-1.387L.713 6.329c.07.163-.135-.133 0 0A3 3 0 0 0 0 8.27a3.03 3.03 0 0 0 2.583 2.986l2.04 10.112h11.113zm-4.859 8.207h13.137l4.75 4.71q-.42.079-.859.08H9.843c-2.18 0-4.055-1.526-4.483-3.644z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 809 B |
1
src/assets/font-icons/crown-wear.svg
Normal file
1
src/assets/font-icons/crown-wear.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="M5.452 25.987c.363 2.18 2.362 3.635 4.543 3.635h12.357c2.18 0 3.997-1.636 4.543-3.635l.181-1.09H5.27zM29.075 5.816c-1.635 0-3.089 1.454-3.089 3.09 0 .908.363 1.635.909 2.18l-4.725 5.452-4.543-8.723c.908-.545 1.635-1.454 1.635-2.726C19.262 3.454 17.81 2 16.173 2c-1.817 0-3.09 1.454-3.09 3.09 0 1.09.728 2.18 1.636 2.725l-4.724 8.723-4.725-5.452a3 3 0 0 0 .908-2.18c0-1.636-1.453-3.09-3.089-3.09S0 7.27 0 8.906c0 1.453 1.09 2.725 2.544 3.089l1.999 10.358H27.44l1.999-10.358c1.453-.182 2.544-1.454 2.544-3.09.181-1.635-1.09-3.089-2.908-3.089"/></svg>
|
||||
|
After Width: | Height: | Size: 644 B |
1
src/assets/font-icons/proof-of-ownership.svg
Normal file
1
src/assets/font-icons/proof-of-ownership.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" fill-rule="evenodd" d="M1.62 14.005a2.115 2.115 0 0 0 0 2.99l3.007 3.008v4.255c0 1.168.947 2.114 2.115 2.114h4.254l3.009 3.009a2.115 2.115 0 0 0 2.99 0l3.009-3.009h4.254a2.115 2.115 0 0 0 2.114-2.114v-4.254l3.009-3.009a2.114 2.114 0 0 0 0-2.99l-3.009-3.009V6.742a2.115 2.115 0 0 0-2.114-2.114h-4.254l-3.009-3.009a2.115 2.115 0 0 0-2.99 0l-3.008 3.009H6.742a2.115 2.115 0 0 0-2.115 2.114v4.255zm5.122 8.138v-3.016L4.61 16.995 3.115 15.5l1.495-1.495 2.132-2.132V6.742h5.131l2.132-2.132L15.5 3.115l1.495 1.495 2.133 2.132h5.13v5.13l2.132 2.133 1.495 1.495-1.495 1.495-2.132 2.133v5.13h-5.13l-2.133 2.132-1.495 1.495-1.495-1.495-2.133-2.132h-5.13zm14.522-8.682a1.058 1.058 0 0 0-1.496-1.495l-5.94 5.94-2.596-2.596a1.057 1.057 0 1 0-1.495 1.495l3.344 3.344a1.057 1.057 0 0 0 1.495 0z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 900 B |
1
src/assets/font-icons/radial-badge.svg
Normal file
1
src/assets/font-icons/radial-badge.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="m1.244 5.276 2.571.67a.24.24 0 0 1 .224.224l.67 2.571c.112.335.56.335.56 0l.67-2.571a.24.24 0 0 1 .224-.224l2.794-.67c.336-.112.336-.56 0-.56l-2.57-.67a.24.24 0 0 1-.224-.223L5.38 1.252c-.112-.336-.559-.336-.559 0l-.67 2.57a.24.24 0 0 1-.224.224l-2.683.67c-.335 0-.335.448 0 .56M14.658 25.397l-3.242-.895a.24.24 0 0 1-.223-.223l-.894-3.13c-.112-.335-.671-.335-.783 0l-.894 3.13a.24.24 0 0 1-.224.223l-3.241.895c-.336.111-.336.67 0 .782l3.241.894a.24.24 0 0 1 .224.224l.894 3.13c.112.335.67.335.783 0l.894-3.13a.24.24 0 0 1 .223-.224l3.242-.894c.447-.112.447-.67 0-.782M31.761 11.983l-2.683-.671h-.112c-.111 0-.111-.112-.111-.224l-.671-2.57c-.112-.336-.559-.336-.559 0l-.112.335-.67 2.235a.24.24 0 0 1-.224.224l-2.571.67c-.335.112-.335.56 0 .56l2.571.67c.112 0 .112.112.224.224l.67 2.57c.112.336.56.336.56 0l.558-1.788.224-.782a.24.24 0 0 1 .223-.224l2.571-.67c.447 0 .447-.448.112-.56"/><path fill="#000" d="m25.948 14.33-8.607 11.29 3.577-12.072c0-.112.112-.224.112-.336h2.012c-.224-.223-.335-.559-.335-.894 0-.559.335-1.006.894-1.23h-2.906l-2.124-5.03h3.688c.336 0 .671.224.895.447l2.794 3.913.56-2.124c0-.112.111-.336.223-.447l-1.9-2.683c-.56-.894-1.565-1.341-2.683-1.341H9.74c.335.223.559.67.559 1.117s-.224.783-.447 1.006h1.006l-2.236 5.142H2.92l1.118-1.565c-.112-.111-.224-.335-.335-.447l-.56-2.124-2.57 3.578c-.783 1.117-.783 2.682.111 3.912l7.043 9.166.782-2.57-5.924-7.714h5.812c0 .112 0 .224.112.335l1.789 6.26c.559.112.894.447 1.117 1.006l.783 2.795 1.677.447-3.13-10.843h8.16l-3.353 11.29c.447.224.67.783.67 1.23 0 .67-.447 1.341-1.118 1.453l-2.794.782-.447.895.223.335c1.342 1.677 3.913 1.677 5.142 0l9.502-12.52c-.112-.112-.224-.335-.224-.447zm-14.867-3.242 1.789-4.47q.335-.672 1.006-.672h1.453q.67 0 1.006.671l1.9 4.471z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
1
src/assets/font-icons/unique-profile.svg
Normal file
1
src/assets/font-icons/unique-profile.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="m4.925 1.225.6 2.3c0 .1.1.2.2.2l2.3.6c.3.1.3.5 0 .5l-2.3.6c-.1 0-.2.1-.2.2l-.6 2.3c-.1.3-.5.3-.5 0l-.7-2.3c0-.1-.1-.2-.2-.2l-2.3-.6c-.3-.1-.3-.5 0-.5l2.3-.6c.1 0 .2-.1.2-.2l.6-2.3c.2-.3.5-.3.6 0M28.325 8.125l.6 2.3c0 .1.1.2.2.2l2.3.6c.3.1.3.5 0 .5l-2.3.6c-.1 0-.2.1-.2.2l-.6 2.3c-.1.3-.5.3-.5 0l-.6-2.3c0-.1-.1-.2-.2-.2l-2.3-.6c-.3-.1-.3-.5 0-.5l2.3-.6c.1 0 .2-.1.2-.2l.6-2.3c0-.2.4-.2.5 0M15.625 9.425c1.9 0 3.5 1.5 3.5 3.5s-1.5 3.5-3.5 3.5-3.5-1.5-3.5-3.5 1.6-3.5 3.5-3.5m0-2c-3 0-5.5 2.4-5.5 5.5s2.4 5.5 5.5 5.5 5.5-2.4 5.5-5.5-2.5-5.5-5.5-5.5M20.525 23.325h-9.7c-.6 0-1-.4-1-1s.4-1 1-1h9.6c.6 0 1 .4 1 1s-.4 1-.9 1M5.925 21.825l.8 2.8c0 .1.1.2.2.2l2.9.8c.3.1.3.6 0 .7l-2.9.8c-.1 0-.2.1-.2.2l-.8 2.8c-.1.3-.6.3-.7 0l-.8-2.8c0-.1-.1-.2-.2-.2l-2.9-.8c-.3-.1-.3-.6 0-.7l2.9-.8c.1 0 .2-.1.2-.2l.8-2.8c.1-.3.6-.3.7 0M8.625 2.325c.8.3 1.4.9 1.6 1.7h14.6c1.2 0 2.2.9 2.4 2.1.2-.1.5-.2.8-.2q.6 0 1.2.3c-.1-2.4-2-4.2-4.4-4.2h-17.4z"/><path fill="#000" d="M28.025 17.025c-.3 0-.5-.1-.8-.1v8c0 1.3-1.1 2.4-2.4 2.4h-13.1c-.3.5-.8.8-1.3 1l-2 .5-.2.5h16.6c2.4 0 4.4-2 4.4-4.4v-8.3c-.3.3-.8.4-1.2.4M2.825 23.225l.5-1.9c.1-.4.3-.8.6-1.1v-10.2c-.7-.3-1.2-.8-1.4-1.6l-.4-1.3h-.1v16.3z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -1437,6 +1437,9 @@
|
||||
"GiftInfoViewUpgraded" = "View Upgraded Gift";
|
||||
"GiftInfoUpgradeBadge" = "upgrade";
|
||||
"GiftInfoUpgradeForFree" = "Upgrade For Free";
|
||||
"GiftInfoWithdraw" = "Withdraw";
|
||||
"GiftInfoWear" = "Wear";
|
||||
"GiftInfoTakeOff" = "Take Off";
|
||||
"GiftInfoTransfer" = "Transfer";
|
||||
"GiftTransferTitle" = "Transfer";
|
||||
"GiftTransferTON" = "Send via Blockchain";
|
||||
@ -1620,3 +1623,13 @@
|
||||
"CheckPasswordTitle" = "Enter Password";
|
||||
"CheckPasswordPlaceholder" = "Password";
|
||||
"CheckPasswordDescription" = "Please enter your password to continue.";
|
||||
"UniqueStatusWearTitle" = "Wear {gift}";
|
||||
"UniqueStatusBenefitsDescription" = "and get these benefits:";
|
||||
"UniqueStatusBadgeBenefitTitle" = "Radiant Badge";
|
||||
"UniqueStatusBadgeDescription" = "The glittering icon of this item will be displayed next to your name.";
|
||||
"UniqueStatusProfileDesignBenefitTitle" = "Unique Profile Design";
|
||||
"UniqueStatusProfileDesignDescription" = "Your profile page will get the color and the symbol of this item.";
|
||||
"UniqueStatusProofOfOwnershipBenefitTitle" = "Proof of Ownership";
|
||||
"UniqueStatusProofOfOwnershipDescription" = "Tapping the icon of this item next to your name will show its info and owner.";
|
||||
"UniqueStatusWearButton" = "Start Wearing";
|
||||
"CollectibleStatusesCategory" = "Collectibles";
|
||||
@ -9,5 +9,6 @@ export { default as GiftModal } from '../components/modals/gift/GiftModal';
|
||||
export { default as GiftRecipientPicker } from '../components/modals/gift/recipient/GiftRecipientPicker';
|
||||
export { default as GiftInfoModal } from '../components/modals/gift/info/GiftInfoModal';
|
||||
export { default as GiftUpgradeModal } from '../components/modals/gift/upgrade/GiftUpgradeModal';
|
||||
export { default as GiftStatusInfoModal } from '../components/modals/gift/status/GiftStatusInfoModal';
|
||||
export { default as GiftWithdrawModal } from '../components/modals/gift/withdraw/GiftWithdrawModal';
|
||||
export { default as GiftTransferModal } from '../components/modals/gift/transfer/GiftTransferModal';
|
||||
|
||||
@ -13,6 +13,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.withSparkles {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sparkles {
|
||||
position: absolute;
|
||||
inset: -0.25rem;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 85%;
|
||||
height: 85%;
|
||||
|
||||
@ -13,6 +13,7 @@ import useDynamicColorListener from '../../hooks/stickers/useDynamicColorListene
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useCustomEmoji from './hooks/useCustomEmoji';
|
||||
|
||||
import Sparkles from './Sparkles';
|
||||
import StickerView from './StickerView';
|
||||
|
||||
import styles from './CustomEmoji.module.scss';
|
||||
@ -41,6 +42,9 @@ type OwnProps = {
|
||||
observeIntersectionForPlaying?: ObserveFn;
|
||||
onClick?: NoneToVoidFunction;
|
||||
onAnimationEnd?: NoneToVoidFunction;
|
||||
withSparkles?: boolean;
|
||||
sparklesClassName?: string;
|
||||
sparklesStyle?: string;
|
||||
};
|
||||
|
||||
const STICKER_SIZE = 20;
|
||||
@ -67,6 +71,9 @@ const CustomEmoji: FC<OwnProps> = ({
|
||||
observeIntersectionForPlaying,
|
||||
onClick,
|
||||
onAnimationEnd,
|
||||
withSparkles,
|
||||
sparklesStyle,
|
||||
sparklesClassName,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
let containerRef = useRef<HTMLDivElement>(null);
|
||||
@ -114,6 +121,7 @@ const CustomEmoji: FC<OwnProps> = ({
|
||||
ref={containerRef}
|
||||
className={buildClassName(
|
||||
styles.root,
|
||||
withSparkles && styles.withSparkles,
|
||||
className,
|
||||
'custom-emoji',
|
||||
'emoji',
|
||||
@ -125,6 +133,16 @@ const CustomEmoji: FC<OwnProps> = ({
|
||||
data-alt={customEmoji?.emoji}
|
||||
style={style}
|
||||
>
|
||||
{withSparkles && (
|
||||
<Sparkles
|
||||
className={buildClassName(
|
||||
styles.sparkles,
|
||||
sparklesClassName,
|
||||
)}
|
||||
style={sparklesStyle}
|
||||
preset="button"
|
||||
/>
|
||||
)}
|
||||
{isSelectable && (
|
||||
<img
|
||||
className={styles.highlightCatch}
|
||||
|
||||
@ -5,11 +5,14 @@ import React, {
|
||||
import { getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type {
|
||||
ApiAvailableReaction, ApiReaction, ApiReactionWithPaid, ApiSticker, ApiStickerSet,
|
||||
ApiAvailableReaction,
|
||||
ApiEmojiStatusType,
|
||||
ApiReaction, ApiReactionWithPaid, ApiSticker, ApiStickerSet,
|
||||
} from '../../api/types';
|
||||
import type { StickerSetOrReactionsSetOrRecent } from '../../types';
|
||||
|
||||
import {
|
||||
COLLECTIBLE_STATUS_SET_ID,
|
||||
FAVORITE_SYMBOL_SET_ID,
|
||||
POPULAR_SYMBOL_SET_ID,
|
||||
RECENT_SYMBOL_SET_ID,
|
||||
@ -28,12 +31,13 @@ import {
|
||||
} from '../../global/selectors';
|
||||
import animateHorizontalScroll from '../../util/animateHorizontalScroll';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { pickTruthy, unique } from '../../util/iteratees';
|
||||
import { pickTruthy, unique, uniqueByField } from '../../util/iteratees';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { REM } from './helpers/mediaDimensions';
|
||||
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
import useHorizontalScroll from '../../hooks/useHorizontalScroll';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useOldLang from '../../hooks/useOldLang';
|
||||
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
|
||||
@ -75,6 +79,7 @@ type StateProps = {
|
||||
customEmojisById?: Record<string, ApiSticker>;
|
||||
recentCustomEmojiIds?: string[];
|
||||
recentStatusEmojis?: ApiSticker[];
|
||||
collectibleStatuses?: ApiEmojiStatusType[];
|
||||
chatEmojiSetId?: string;
|
||||
topReactions?: ApiReaction[];
|
||||
recentReactions?: ApiReaction[];
|
||||
@ -114,6 +119,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
recentCustomEmojiIds,
|
||||
selectedReactionIds,
|
||||
recentStatusEmojis,
|
||||
collectibleStatuses,
|
||||
stickerSetsById,
|
||||
chatEmojiSetId,
|
||||
topReactions,
|
||||
@ -160,6 +166,11 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
: Object.values(pickTruthy(customEmojisById!, recentCustomEmojiIds!));
|
||||
}, [customEmojisById, isStatusPicker, recentCustomEmojiIds, recentStatusEmojis]);
|
||||
|
||||
const collectibleStatusEmojis = useMemo(() => {
|
||||
const collectibleStatusEmojiIds = collectibleStatuses?.map((status) => status.documentId);
|
||||
return customEmojisById && collectibleStatusEmojiIds?.map((id) => customEmojisById[id]).filter(Boolean);
|
||||
}, [customEmojisById, collectibleStatuses]);
|
||||
|
||||
const prefix = `${idPrefix}-custom-emoji`;
|
||||
const {
|
||||
activeSetIndex,
|
||||
@ -172,7 +183,8 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const canLoadAndPlay = usePrevDuringAnimation(loadAndPlay || undefined, SLIDE_TRANSITION_DURATION);
|
||||
|
||||
const lang = useOldLang();
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
|
||||
const areAddedLoaded = Boolean(addedCustomEmojiIds);
|
||||
|
||||
@ -184,7 +196,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
defaultSets.push({
|
||||
id: TOP_SYMBOL_SET_ID,
|
||||
accessHash: '',
|
||||
title: lang('PremiumPreviewTags'),
|
||||
title: oldLang('PremiumPreviewTags'),
|
||||
reactions: defaultTagReactions,
|
||||
count: defaultTagReactions.length,
|
||||
isEmoji: true,
|
||||
@ -201,7 +213,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
defaultSets.push({
|
||||
id: TOP_SYMBOL_SET_ID,
|
||||
accessHash: '',
|
||||
title: lang('Reactions'),
|
||||
title: oldLang('Reactions'),
|
||||
reactions: topReactionsSlice,
|
||||
count: topReactionsSlice.length,
|
||||
isEmoji: true,
|
||||
@ -224,7 +236,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
defaultSets.push({
|
||||
id: isPopular ? POPULAR_SYMBOL_SET_ID : RECENT_SYMBOL_SET_ID,
|
||||
accessHash: '',
|
||||
title: lang(isPopular ? 'PopularReactions' : 'RecentStickers'),
|
||||
title: oldLang(isPopular ? 'PopularReactions' : 'RecentStickers'),
|
||||
reactions: allRecentReactions,
|
||||
count: allRecentReactions.length,
|
||||
isEmoji: true,
|
||||
@ -233,15 +245,26 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
} else if (isStatusPicker) {
|
||||
const defaultStatusIconsPack = stickerSetsById[defaultStatusIconsId!];
|
||||
if (defaultStatusIconsPack?.stickers?.length) {
|
||||
const stickers = defaultStatusIconsPack.stickers
|
||||
const stickers = uniqueByField(defaultStatusIconsPack.stickers
|
||||
.slice(0, RECENT_DEFAULT_STATUS_COUNT)
|
||||
.concat(recentCustomEmojis || []);
|
||||
.concat(recentCustomEmojis || []), 'id');
|
||||
defaultSets.push({
|
||||
...defaultStatusIconsPack,
|
||||
stickers,
|
||||
count: stickers.length,
|
||||
id: RECENT_SYMBOL_SET_ID,
|
||||
title: lang('RecentStickers'),
|
||||
title: oldLang('RecentStickers'),
|
||||
isEmoji: true,
|
||||
});
|
||||
}
|
||||
if (collectibleStatusEmojis?.length) {
|
||||
defaultSets.push({
|
||||
id: COLLECTIBLE_STATUS_SET_ID,
|
||||
accessHash: '',
|
||||
count: collectibleStatusEmojis.length,
|
||||
stickers: collectibleStatusEmojis,
|
||||
title: lang('CollectibleStatusesCategory'),
|
||||
isEmoji: true,
|
||||
});
|
||||
}
|
||||
} else if (withDefaultTopicIcons) {
|
||||
@ -250,14 +273,14 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
defaultSets.push({
|
||||
...defaultTopicIconsPack,
|
||||
id: RECENT_SYMBOL_SET_ID,
|
||||
title: lang('RecentStickers'),
|
||||
title: oldLang('RecentStickers'),
|
||||
});
|
||||
}
|
||||
} else if (recentCustomEmojis?.length) {
|
||||
defaultSets.push({
|
||||
id: RECENT_SYMBOL_SET_ID,
|
||||
accessHash: '0',
|
||||
title: lang('RecentStickers'),
|
||||
title: oldLang('RecentStickers'),
|
||||
stickers: recentCustomEmojis,
|
||||
count: recentCustomEmojis.length,
|
||||
isEmoji: true,
|
||||
@ -279,9 +302,9 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
];
|
||||
}, [
|
||||
addedCustomEmojiIds, isReactionPicker, isStatusPicker, withDefaultTopicIcons, recentCustomEmojis,
|
||||
customEmojiFeaturedIds, stickerSetsById, topReactions, availableReactions, lang, recentReactions,
|
||||
customEmojiFeaturedIds, stickerSetsById, topReactions, availableReactions, oldLang, recentReactions,
|
||||
defaultStatusIconsId, defaultTopicIconsId, isSavedMessages, defaultTagReactions, chatEmojiSetId,
|
||||
isWithPaidReaction,
|
||||
isWithPaidReaction, collectibleStatusEmojis, lang,
|
||||
]);
|
||||
|
||||
const noPopulatedSets = useMemo(() => (
|
||||
@ -383,7 +406,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
return (
|
||||
<div className={fullClassName}>
|
||||
{noPopulatedSets ? (
|
||||
<div className={pickerStyles.pickerDisabled}>{lang('NoStickers')}</div>
|
||||
<div className={pickerStyles.pickerDisabled}>{oldLang('NoStickers')}</div>
|
||||
) : (
|
||||
<Loading />
|
||||
)}
|
||||
@ -487,11 +510,13 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const isSavedMessages = Boolean(chatId && selectIsChatWithSelf(global, chatId));
|
||||
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
|
||||
const collectibleStatuses = global.collectibleEmojiStatuses?.statuses;
|
||||
|
||||
return {
|
||||
customEmojisById: !isStatusPicker ? customEmojisById : undefined,
|
||||
customEmojisById,
|
||||
recentCustomEmojiIds: !isStatusPicker ? recentCustomEmojiIds : undefined,
|
||||
recentStatusEmojis: isStatusPicker ? recentStatusEmojis : undefined,
|
||||
collectibleStatuses: isStatusPicker ? collectibleStatuses : undefined,
|
||||
stickerSetsById,
|
||||
addedCustomEmojiIds: global.customEmojis.added.setIds,
|
||||
canAnimate: selectCanPlayAnimatedEmojis(global),
|
||||
|
||||
@ -6,12 +6,23 @@
|
||||
:global(.custom-emoji) {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
:global(.statusSparkles) {
|
||||
color: var(--accent-color);
|
||||
:global(.selected) & {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fullName {
|
||||
font-size: 1em;
|
||||
margin-bottom: 0;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
&.canCopy {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
isPeerUser,
|
||||
} from '../../global/helpers';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import buildStyle from '../../util/buildStyle';
|
||||
import { copyTextToClipboard } from '../../util/clipboard';
|
||||
import stopEvent from '../../util/stopEvent';
|
||||
import renderText from './helpers/renderText';
|
||||
@ -47,6 +48,7 @@ type OwnProps = {
|
||||
iconElement?: React.ReactNode;
|
||||
onEmojiStatusClick?: NoneToVoidFunction;
|
||||
observeIntersection?: ObserveFn;
|
||||
statusSparklesColor?: string;
|
||||
};
|
||||
|
||||
const FullNameTitle: FC<OwnProps> = ({
|
||||
@ -63,6 +65,7 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
iconElement,
|
||||
onEmojiStatusClick,
|
||||
observeIntersection,
|
||||
statusSparklesColor,
|
||||
}) => {
|
||||
const lang = useOldLang();
|
||||
const { showNotification } = getActions();
|
||||
@ -72,6 +75,7 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
const title = realPeer && (isUser ? getUserFullName(realPeer) : getChatTitle(lang, realPeer));
|
||||
const isPremium = isUser && realPeer.isPremium;
|
||||
const canShowEmojiStatus = withEmojiStatus && !isSavedMessages && realPeer;
|
||||
const emojiStatus = realPeer?.emojiStatus;
|
||||
|
||||
const handleTitleClick = useLastCallback((e) => {
|
||||
if (!title || !canCopyTitle) {
|
||||
@ -134,17 +138,20 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
<>
|
||||
{!noVerified && peer?.isVerified && <VerifiedIcon />}
|
||||
{!noFake && peer?.fakeType && <FakeIcon fakeType={peer.fakeType} />}
|
||||
{canShowEmojiStatus && realPeer.emojiStatus && (
|
||||
{canShowEmojiStatus && emojiStatus && (
|
||||
<Transition
|
||||
className={styles.transition}
|
||||
activeKey={Number(realPeer.emojiStatus.documentId)}
|
||||
activeKey={Number(emojiStatus.documentId)}
|
||||
name="fade"
|
||||
shouldCleanup
|
||||
shouldRestoreHeight
|
||||
>
|
||||
<CustomEmoji
|
||||
forceAlways
|
||||
documentId={realPeer.emojiStatus.documentId}
|
||||
withSparkles={emojiStatus.type === 'collectible'}
|
||||
sparklesClassName="statusSparkles"
|
||||
sparklesStyle={buildStyle(statusSparklesColor && `color: ${statusSparklesColor}`)}
|
||||
documentId={emojiStatus.documentId}
|
||||
size={emojiStatusSize}
|
||||
loopLimit={!noLoopLimit ? EMOJI_STATUS_LOOP_LIMIT : undefined}
|
||||
observeIntersectionForLoading={observeIntersection}
|
||||
@ -152,7 +159,7 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
/>
|
||||
</Transition>
|
||||
)}
|
||||
{canShowEmojiStatus && !realPeer.emojiStatus && isPremium && <StarIcon />}
|
||||
{canShowEmojiStatus && !emojiStatus && isPremium && <StarIcon />}
|
||||
</>
|
||||
)}
|
||||
{iconElement}
|
||||
|
||||
@ -3,13 +3,12 @@
|
||||
align-items: center;
|
||||
background: var(--color-chat-hover);
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
margin-inline: 0.25rem;
|
||||
padding-right: 0.75rem;
|
||||
border-radius: 1rem;
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
flex-shrink: 1;
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
@ -71,7 +70,7 @@
|
||||
.name {
|
||||
margin-left: 0.5rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
:global(.emoji.emoji-small) {
|
||||
|
||||
@ -144,6 +144,10 @@
|
||||
justify-content: flex-end;
|
||||
pointer-events: none;
|
||||
|
||||
:global(.statusSparkles) {
|
||||
color: var(--color-white) !important;
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
.status {
|
||||
text-align: right;
|
||||
|
||||
@ -57,6 +57,7 @@ type StateProps =
|
||||
topic?: ApiTopic;
|
||||
messagesCount?: number;
|
||||
emojiStatusSticker?: ApiSticker;
|
||||
emojiStatusSlug?: string;
|
||||
profilePhotos?: ApiPeerPhotos;
|
||||
};
|
||||
|
||||
@ -77,6 +78,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
topic,
|
||||
messagesCount,
|
||||
emojiStatusSticker,
|
||||
emojiStatusSlug,
|
||||
profilePhotos,
|
||||
peerId,
|
||||
}) => {
|
||||
@ -86,6 +88,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
openStickerSet,
|
||||
openPrivacySettingsNoticeModal,
|
||||
loadMoreProfilePhotos,
|
||||
openUniqueGiftBySlug,
|
||||
} = getActions();
|
||||
|
||||
const lang = useOldLang();
|
||||
@ -137,6 +140,10 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleStatusClick = useLastCallback(() => {
|
||||
if (emojiStatusSlug) {
|
||||
openUniqueGiftBySlug({ slug: emojiStatusSlug });
|
||||
return;
|
||||
}
|
||||
if (!peerId) {
|
||||
openStickerSet({
|
||||
stickerSetInfo: emojiStatusSticker!.stickerSetInfo,
|
||||
@ -383,6 +390,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const emojiStatus = (user || chat)?.emojiStatus;
|
||||
const emojiStatusSticker = emojiStatus ? global.customEmojis.byId[emojiStatus.documentId] : undefined;
|
||||
const emojiStatusSlug = emojiStatus?.type === 'collectible' ? emojiStatus.slug : undefined;
|
||||
|
||||
return {
|
||||
user,
|
||||
@ -391,6 +399,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
mediaIndex,
|
||||
avatarOwnerId,
|
||||
emojiStatusSticker,
|
||||
emojiStatusSlug,
|
||||
profilePhotos,
|
||||
...(topic && {
|
||||
topic,
|
||||
|
||||
@ -17,6 +17,7 @@ type PresetParameters = ButtonParameters | ProgressParameters;
|
||||
|
||||
type OwnProps = {
|
||||
className?: string;
|
||||
style?: string;
|
||||
} & PresetParameters;
|
||||
|
||||
const SYMBOL = '✦';
|
||||
@ -83,11 +84,12 @@ const PROGRESS_POSITIONS = generateRandomProgressPositions(100);
|
||||
|
||||
const Sparkles = ({
|
||||
className,
|
||||
style,
|
||||
...presetSettings
|
||||
}: OwnProps) => {
|
||||
if (presetSettings.preset === 'button') {
|
||||
return (
|
||||
<div className={buildClassName(styles.root, styles.button, className)}>
|
||||
<div className={buildClassName(styles.root, styles.button, className)} style={style}>
|
||||
{BUTTON_POSITIONS.map((position) => {
|
||||
const shiftX = Math.cos(Math.atan2(-50 + position.y, -50 + position.x)) * 100;
|
||||
const shiftY = Math.sin(Math.atan2(-50 + position.y, -50 + position.x)) * 100;
|
||||
@ -113,7 +115,7 @@ const Sparkles = ({
|
||||
|
||||
if (presetSettings.preset === 'progress') {
|
||||
return (
|
||||
<div className={buildClassName(styles.root, styles.progress, className)}>
|
||||
<div className={buildClassName(styles.root, styles.progress, className)} style={style}>
|
||||
{PROGRESS_POSITIONS.map((position) => {
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -22,6 +22,7 @@ import Button from '../ui/Button';
|
||||
import Menu from '../ui/Menu';
|
||||
import MenuItem from '../ui/MenuItem';
|
||||
import Icon from './icons/Icon';
|
||||
import Sparkles from './Sparkles';
|
||||
import StickerView from './StickerView';
|
||||
|
||||
import './StickerButton.scss';
|
||||
@ -54,6 +55,7 @@ type OwnProps<T> = {
|
||||
onContextMenuClose?: NoneToVoidFunction;
|
||||
onContextMenuClick?: NoneToVoidFunction;
|
||||
isEffectEmoji?: boolean;
|
||||
withSparkles?: boolean;
|
||||
};
|
||||
|
||||
const contentForStatusMenuContext = [
|
||||
@ -92,6 +94,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
onContextMenuClose,
|
||||
onContextMenuClick,
|
||||
isEffectEmoji,
|
||||
withSparkles,
|
||||
}: OwnProps<T>) => {
|
||||
const { openStickerSet, openPremiumModal, setEmojiStatus } = getActions();
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -198,8 +201,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
handleContextMenuClose();
|
||||
onContextMenuClick?.();
|
||||
setEmojiStatus({
|
||||
emojiStatusId: sticker.id,
|
||||
expires: getServerTime() + duration,
|
||||
emojiStatus: { type: 'regular', documentId: sticker.id, until: getServerTime() + duration },
|
||||
});
|
||||
});
|
||||
|
||||
@ -289,6 +291,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
onClick={handleClick}
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
{withSparkles && <Sparkles preset="button" /> }
|
||||
{isIntesectingForShowing && (
|
||||
<StickerView
|
||||
containerRef={ref}
|
||||
|
||||
@ -2,13 +2,16 @@ import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useMemo, useRef, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, getGlobal } from '../../global';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiAvailableReaction, ApiReactionWithPaid, ApiSticker } from '../../api/types';
|
||||
import type {
|
||||
ApiAvailableReaction, ApiEmojiStatusType, ApiReactionWithPaid, ApiSticker,
|
||||
} from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
import type { StickerSetOrReactionsSetOrRecent } from '../../types';
|
||||
|
||||
import {
|
||||
COLLECTIBLE_STATUS_SET_ID,
|
||||
DEFAULT_STATUS_ICON_ID,
|
||||
DEFAULT_TOPIC_ICON_STICKER_ID,
|
||||
EFFECT_EMOJIS_SET_ID,
|
||||
@ -75,13 +78,17 @@ type OwnProps = {
|
||||
onContextMenuClick?: NoneToVoidFunction;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
collectibleStatuses?: ApiEmojiStatusType[];
|
||||
};
|
||||
|
||||
const ITEMS_PER_ROW_FALLBACK = 8;
|
||||
const ITEMS_MOBILE_PER_ROW_FALLBACK = 7;
|
||||
const ITEMS_MINI_MOBILE_PER_ROW_FALLBACK = 6;
|
||||
const MOBILE_WIDTH_THRESHOLD_PX = 440;
|
||||
const MINI_MOBILE_WIDTH_THRESHOLD_PX = 362;
|
||||
|
||||
const StickerSet: FC<OwnProps> = ({
|
||||
const StickerSet: FC<OwnProps & StateProps> = ({
|
||||
stickerSet,
|
||||
loadAndPlay,
|
||||
index,
|
||||
@ -114,6 +121,7 @@ const StickerSet: FC<OwnProps> = ({
|
||||
onContextMenuOpen,
|
||||
onContextMenuClose,
|
||||
onContextMenuClick,
|
||||
collectibleStatuses,
|
||||
}) => {
|
||||
const {
|
||||
clearRecentStickers,
|
||||
@ -149,6 +157,7 @@ const StickerSet: FC<OwnProps> = ({
|
||||
const emojiMarginPx = isMobile ? 8 : 10;
|
||||
const emojiVerticalMarginPx = isMobile ? 8 : 4;
|
||||
const isRecent = stickerSet.id === RECENT_SYMBOL_SET_ID;
|
||||
const isStatusCollectible = stickerSet.id === COLLECTIBLE_STATUS_SET_ID;
|
||||
const isFavorite = stickerSet.id === FAVORITE_SYMBOL_SET_ID;
|
||||
const isPopular = stickerSet.id === POPULAR_SYMBOL_SET_ID;
|
||||
const isEmoji = stickerSet.isEmoji;
|
||||
@ -255,7 +264,11 @@ const StickerSet: FC<OwnProps> = ({
|
||||
const favoriteStickerIdsSet = useMemo(() => (
|
||||
favoriteStickers ? new Set(favoriteStickers.map(({ id }) => id)) : undefined
|
||||
), [favoriteStickers]);
|
||||
const withAddSetButton = !shouldHideHeader && !isRecent && isEmoji && !isPopular && !isChatEmojiSet
|
||||
const collectibleEmojiIdsSet = useMemo(() => (
|
||||
collectibleStatuses ? new Set(collectibleStatuses.map(({ documentId }) => documentId)) : undefined
|
||||
), [collectibleStatuses]);
|
||||
const withAddSetButton = !shouldHideHeader && !isRecent && !isStatusCollectible
|
||||
&& isEmoji && !isPopular && !isChatEmojiSet
|
||||
&& (!isInstalled || (!isCurrentUserPremium && !isSavedMessages));
|
||||
const addSetButtonText = useMemo(() => {
|
||||
if (isLocked) {
|
||||
@ -370,6 +383,9 @@ const StickerSet: FC<OwnProps> = ({
|
||||
const reactionId = sticker.isCustomEmoji ? sticker.id : sticker.emoji;
|
||||
const isSelected = reactionId ? selectedReactionIds?.includes(reactionId) : undefined;
|
||||
|
||||
const withSparkles = sticker.id === COLLECTIBLE_STATUS_SET_ID
|
||||
|| collectibleEmojiIdsSet?.has(sticker.id);
|
||||
|
||||
return (
|
||||
<StickerButton
|
||||
key={sticker.id}
|
||||
@ -399,6 +415,7 @@ const StickerSet: FC<OwnProps> = ({
|
||||
isEffectEmoji={stickerSet.id === EFFECT_EMOJIS_SET_ID}
|
||||
noShowPremium={isCurrentUserPremium
|
||||
&& (stickerSet.id === EFFECT_STICKERS_SET_ID || stickerSet.id === EFFECT_EMOJIS_SET_ID)}
|
||||
withSparkles={withSparkles}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -428,7 +445,13 @@ const StickerSet: FC<OwnProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(StickerSet);
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const collectibleStatuses = global.collectibleEmojiStatuses?.statuses;
|
||||
|
||||
return { collectibleStatuses };
|
||||
},
|
||||
)(StickerSet));
|
||||
|
||||
function getItemsPerRowFallback(windowWidth: number): number {
|
||||
return windowWidth > MOBILE_WIDTH_THRESHOLD_PX
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useMemo } from '../../../lib/teact/teact';
|
||||
|
||||
import type { ApiEmojiStatus, ApiReactionCustomEmoji } from '../../../api/types';
|
||||
import type { ApiEmojiStatusType, ApiReactionCustomEmoji } from '../../../api/types';
|
||||
|
||||
import { getStickerHashById } from '../../../global/helpers';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -15,7 +15,7 @@ import CustomEmoji from '../CustomEmoji';
|
||||
import styles from './CustomEmojiEffect.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
reaction: ApiReactionCustomEmoji | ApiEmojiStatus;
|
||||
reaction: ApiReactionCustomEmoji | ApiEmojiStatusType;
|
||||
className?: string;
|
||||
isLottie?: boolean;
|
||||
particleSize?: number;
|
||||
|
||||
@ -117,6 +117,7 @@
|
||||
}
|
||||
|
||||
.emoji-status {
|
||||
overflow: visible;
|
||||
--custom-emoji-size: 1.5rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useRef } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiEmojiStatus, ApiSticker } from '../../../api/types';
|
||||
import type { ApiEmojiStatusCollectible, ApiEmojiStatusType, ApiSticker } from '../../../api/types';
|
||||
|
||||
import { EMOJI_STATUS_LOOP_LIMIT } from '../../../config';
|
||||
import { selectUser } from '../../../global/selectors';
|
||||
@ -20,13 +20,14 @@ import Button from '../../ui/Button';
|
||||
import StatusPickerMenu from './StatusPickerMenu.async';
|
||||
|
||||
interface StateProps {
|
||||
emojiStatus?: ApiEmojiStatus;
|
||||
emojiStatus?: ApiEmojiStatusType;
|
||||
collectibleStatuses?: ApiEmojiStatusType[];
|
||||
}
|
||||
|
||||
const EFFECT_DURATION_MS = 1500;
|
||||
const EMOJI_STATUS_SIZE = 24;
|
||||
|
||||
const StatusButton: FC<StateProps> = ({ emojiStatus }) => {
|
||||
const StatusButton: FC<StateProps> = ({ emojiStatus, collectibleStatuses }) => {
|
||||
const { setEmojiStatus, loadCurrentUser } = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -47,9 +48,14 @@ const StatusButton: FC<StateProps> = ({ emojiStatus }) => {
|
||||
}, [emojiStatus, shouldShowEffect, showEffect, unmarkShouldShowEffect]);
|
||||
|
||||
const handleEmojiStatusSet = useCallback((sticker: ApiSticker) => {
|
||||
const collectibleStatus = collectibleStatuses?.find(
|
||||
((status) => 'collectibleId' in status && status.documentId === sticker.id),
|
||||
) as ApiEmojiStatusCollectible | undefined;
|
||||
markShouldShowEffect();
|
||||
setEmojiStatus({ emojiStatusId: sticker.id });
|
||||
}, [markShouldShowEffect, setEmojiStatus]);
|
||||
setEmojiStatus({
|
||||
emojiStatus: collectibleStatus || { type: 'regular', documentId: sticker.id },
|
||||
});
|
||||
}, [markShouldShowEffect, setEmojiStatus, collectibleStatuses]);
|
||||
|
||||
useTimeout(hideEffect, isEffectShown ? EFFECT_DURATION_MS : undefined);
|
||||
|
||||
@ -81,6 +87,7 @@ const StatusButton: FC<StateProps> = ({ emojiStatus }) => {
|
||||
documentId={emojiStatus.documentId}
|
||||
size={EMOJI_STATUS_SIZE}
|
||||
loopLimit={EMOJI_STATUS_LOOP_LIMIT}
|
||||
withSparkles={emojiStatus?.type === 'collectible'}
|
||||
/>
|
||||
) : <StarIcon />}
|
||||
</Button>
|
||||
@ -97,8 +104,10 @@ const StatusButton: FC<StateProps> = ({ emojiStatus }) => {
|
||||
export default memo(withGlobal((global): StateProps => {
|
||||
const { currentUserId } = global;
|
||||
const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined;
|
||||
const collectibleStatuses = global.collectibleEmojiStatuses?.statuses;
|
||||
|
||||
return {
|
||||
emojiStatus: currentUser?.emojiStatus,
|
||||
collectibleStatuses,
|
||||
};
|
||||
})(StatusButton));
|
||||
|
||||
@ -230,6 +230,7 @@ const Main = ({
|
||||
openThread,
|
||||
toggleLeftColumn,
|
||||
loadRecentEmojiStatuses,
|
||||
loadUserCollectibleStatuses,
|
||||
updatePageTitle,
|
||||
loadTopReactions,
|
||||
loadRecentReactions,
|
||||
@ -335,6 +336,7 @@ const Main = ({
|
||||
loadTopBotApps();
|
||||
loadPaidReactionPrivacy();
|
||||
loadPasswordInfo();
|
||||
loadUserCollectibleStatuses();
|
||||
}
|
||||
}, [isMasterTab, isSynced]);
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
|
||||
.chat-info-wrapper {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.header-tools {
|
||||
@ -154,7 +154,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
||||
@ -85,6 +85,7 @@ type StateProps = {
|
||||
isSyncing?: boolean;
|
||||
isFetchingDifference?: boolean;
|
||||
emojiStatusSticker?: ApiSticker;
|
||||
emojiStatusSlug?: string;
|
||||
};
|
||||
|
||||
const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
@ -108,6 +109,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
getCurrentPinnedIndex,
|
||||
getLoadingPinnedId,
|
||||
emojiStatusSticker,
|
||||
emojiStatusSlug,
|
||||
isSavedDialog,
|
||||
onFocusPinnedMessage,
|
||||
}) => {
|
||||
@ -120,6 +122,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
openPremiumModal,
|
||||
openStickerSet,
|
||||
updateMiddleSearch,
|
||||
openUniqueGiftBySlug,
|
||||
} = getActions();
|
||||
|
||||
const lang = useOldLang();
|
||||
@ -165,10 +168,18 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleUserStatusClick = useLastCallback(() => {
|
||||
if (emojiStatusSlug) {
|
||||
openUniqueGiftBySlug({ slug: emojiStatusSlug });
|
||||
return;
|
||||
}
|
||||
openPremiumModal({ fromUserId: chatId });
|
||||
});
|
||||
|
||||
const handleChannelStatusClick = useLastCallback(() => {
|
||||
if (emojiStatusSlug) {
|
||||
openUniqueGiftBySlug({ slug: emojiStatusSlug });
|
||||
return;
|
||||
}
|
||||
openStickerSet({
|
||||
stickerSetInfo: emojiStatusSticker!.stickerSetInfo,
|
||||
});
|
||||
@ -388,6 +399,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const emojiStatus = chat?.emojiStatus;
|
||||
const emojiStatusSticker = emojiStatus && global.customEmojis.byId[emojiStatus.documentId];
|
||||
const emojiStatusSlug = emojiStatus?.type === 'collectible' ? emojiStatus.slug : undefined;
|
||||
|
||||
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
||||
|
||||
@ -406,6 +418,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isSyncing: global.isSyncing,
|
||||
isFetchingDifference: global.isFetchingDifference,
|
||||
emojiStatusSticker,
|
||||
emojiStatusSlug,
|
||||
isSavedDialog,
|
||||
};
|
||||
},
|
||||
|
||||
@ -18,6 +18,7 @@ import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.a
|
||||
import PremiumGiftModal from './gift/GiftModal.async';
|
||||
import GiftInfoModal from './gift/info/GiftInfoModal.async';
|
||||
import GiftRecipientPicker from './gift/recipient/GiftRecipientPicker.async';
|
||||
import GiftStatusInfoModal from './gift/status/GiftStatusInfoModal.async';
|
||||
import GiftTransferModal from './gift/transfer/GiftTransferModal.async';
|
||||
import GiftUpgradeModal from './gift/upgrade/GiftUpgradeModal.async';
|
||||
import GiftWithdrawModal from './gift/withdraw/GiftWithdrawModal.async';
|
||||
@ -71,6 +72,7 @@ type ModalKey = keyof Pick<TabState,
|
||||
'giftUpgradeModal' |
|
||||
'monetizationVerificationModal' |
|
||||
'giftWithdrawModal' |
|
||||
'giftStatusInfoModal' |
|
||||
'giftTransferModal'
|
||||
>;
|
||||
|
||||
@ -117,6 +119,7 @@ const MODALS: ModalRegistry = {
|
||||
giftUpgradeModal: GiftUpgradeModal,
|
||||
monetizationVerificationModal: VerificationMonetizationModal,
|
||||
giftWithdrawModal: GiftWithdrawModal,
|
||||
giftStatusInfoModal: GiftStatusInfoModal,
|
||||
giftTransferModal: GiftTransferModal,
|
||||
};
|
||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||
|
||||
@ -28,6 +28,10 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.listItemIcon {
|
||||
color: var(--accent-color) !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -2,6 +2,8 @@ import React, { memo, type TeactNode } from '../../../lib/teact/teact';
|
||||
|
||||
import type { IconName } from '../../../types/icons';
|
||||
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
|
||||
import Icon from '../../common/icons/Icon';
|
||||
import Button from '../../ui/Button';
|
||||
import ListItem from '../../ui/ListItem';
|
||||
@ -13,6 +15,7 @@ import styles from './TableAboutModal.module.scss';
|
||||
export type TableAboutData = [IconName | undefined, TeactNode, TeactNode][];
|
||||
|
||||
type OwnProps = {
|
||||
contentClassName?: string;
|
||||
isOpen?: boolean;
|
||||
listItemData?: TableAboutData;
|
||||
headerIconName?: IconName;
|
||||
@ -36,11 +39,12 @@ const TableAboutModal = ({
|
||||
withSeparator,
|
||||
onClose,
|
||||
onButtonClick,
|
||||
contentClassName,
|
||||
}: OwnProps) => {
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
className={styles.root}
|
||||
className={buildClassName(styles.root, contentClassName)}
|
||||
contentClassName={styles.content}
|
||||
hasAbsoluteCloseButton
|
||||
absoluteCloseButtonColor={hasBackdrop ? 'translucent-white' : undefined}
|
||||
@ -55,6 +59,7 @@ const TableAboutModal = ({
|
||||
isStatic
|
||||
multiline
|
||||
icon={icon}
|
||||
iconClassName={styles.listItemIcon}
|
||||
>
|
||||
<span className="title">{title}</span>
|
||||
<span className="subtitle">{subtitle}</span>
|
||||
|
||||
@ -70,6 +70,7 @@ const EmojiStatusAccessModal: FC<OwnProps & StateProps> = ({
|
||||
return {
|
||||
...currentUser,
|
||||
emojiStatus: {
|
||||
type: 'regular',
|
||||
documentId: stickerSet.stickers[currentStatusIndex].id,
|
||||
},
|
||||
} satisfies ApiUser;
|
||||
|
||||
@ -62,7 +62,7 @@
|
||||
}
|
||||
|
||||
.modalHeader {
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
padding: 0.375rem;
|
||||
position: absolute;
|
||||
|
||||
@ -3,14 +3,16 @@ import React, { memo, useMemo } from '../../../../lib/teact/teact';
|
||||
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
||||
|
||||
import type {
|
||||
ApiEmojiStatusCollectible,
|
||||
ApiEmojiStatusType,
|
||||
ApiPeer,
|
||||
} from '../../../../api/types';
|
||||
import type { TabState } from '../../../../global/types';
|
||||
|
||||
import { TME_LINK_PREFIX } from '../../../../config';
|
||||
import { DEFAULT_STATUS_ICON_ID, TME_LINK_PREFIX } from '../../../../config';
|
||||
import { getHasAdminRight, getPeerTitle } from '../../../../global/helpers';
|
||||
import { isApiPeerChat } from '../../../../global/helpers/peers';
|
||||
import { selectPeer } from '../../../../global/selectors';
|
||||
import { selectPeer, selectUser } from '../../../../global/selectors';
|
||||
import buildClassName from '../../../../util/buildClassName';
|
||||
import { copyTextToClipboard } from '../../../../util/clipboard';
|
||||
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
||||
@ -51,6 +53,8 @@ type StateProps = {
|
||||
currentUserId?: string;
|
||||
starGiftMaxConvertPeriod?: number;
|
||||
hasAdminRights?: boolean;
|
||||
currentUserEmojiStatus?: ApiEmojiStatusType;
|
||||
collectibleEmojiStatuses?: ApiEmojiStatusType[];
|
||||
};
|
||||
|
||||
const STICKER_SIZE = 120;
|
||||
@ -62,6 +66,8 @@ const GiftInfoModal = ({
|
||||
currentUserId,
|
||||
starGiftMaxConvertPeriod,
|
||||
hasAdminRights,
|
||||
currentUserEmojiStatus,
|
||||
collectibleEmojiStatuses,
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
closeGiftInfoModal,
|
||||
@ -72,6 +78,8 @@ const GiftInfoModal = ({
|
||||
openGiftUpgradeModal,
|
||||
showNotification,
|
||||
openChatWithDraft,
|
||||
openGiftStatusInfoModal,
|
||||
setEmojiStatus,
|
||||
openGiftTransferModal,
|
||||
} = getActions();
|
||||
|
||||
@ -99,6 +107,25 @@ const GiftInfoModal = ({
|
||||
const gift = isSavedGift ? typeGift.gift : typeGift;
|
||||
const giftSticker = gift && getStickerFromGift(gift);
|
||||
|
||||
const currenUniqueEmojiStatusSlug = currentUserEmojiStatus?.type === 'collectible'
|
||||
? currentUserEmojiStatus.slug : undefined;
|
||||
|
||||
const starGiftUniqueSlug = gift?.type === 'starGiftUnique' ? gift.slug : undefined;
|
||||
const starGiftUniqueLink = useMemo(() => {
|
||||
if (!starGiftUniqueSlug) return undefined;
|
||||
return `${TME_LINK_PREFIX}nft/${starGiftUniqueSlug}`;
|
||||
}, [starGiftUniqueSlug]);
|
||||
const userCollectibleStatus = useMemo(() => {
|
||||
if (!starGiftUniqueSlug) return undefined;
|
||||
return collectibleEmojiStatuses?.find((
|
||||
status,
|
||||
) => status.type === 'collectible' && status.slug === starGiftUniqueSlug) as ApiEmojiStatusCollectible | undefined;
|
||||
}, [starGiftUniqueSlug, collectibleEmojiStatuses]);
|
||||
|
||||
const isGiftUnique = gift && gift.type === 'starGiftUnique';
|
||||
const canTakeOff = isGiftUnique && currenUniqueEmojiStatusSlug === gift.slug;
|
||||
const canWear = userCollectibleStatus && !canTakeOff;
|
||||
|
||||
const canFocusUpgrade = Boolean(savedGift?.upgradeMsgId);
|
||||
const canUpdate = !canFocusUpgrade && savedGift?.inputGift && (
|
||||
isTargetChat ? hasAdminRights : renderingTargetPeer?.id === currentUserId
|
||||
@ -108,12 +135,6 @@ const GiftInfoModal = ({
|
||||
closeGiftInfoModal();
|
||||
});
|
||||
|
||||
const starGiftUniqueLink = useMemo(() => {
|
||||
const slug = gift?.type === 'starGiftUnique' ? gift.slug : undefined;
|
||||
if (!slug) return undefined;
|
||||
return `${TME_LINK_PREFIX}nft/${slug}`;
|
||||
}, [gift]);
|
||||
|
||||
const handleCopyLink = useLastCallback(() => {
|
||||
if (!starGiftUniqueLink) return;
|
||||
copyTextToClipboard(starGiftUniqueLink);
|
||||
@ -133,6 +154,19 @@ const GiftInfoModal = ({
|
||||
openGiftTransferModal({ gift: savedGift });
|
||||
});
|
||||
|
||||
const handleWear = useLastCallback(() => {
|
||||
if (gift?.type !== 'starGiftUnique' || !userCollectibleStatus) return;
|
||||
openGiftStatusInfoModal({ emojiStatus: userCollectibleStatus });
|
||||
});
|
||||
|
||||
const handleTakeOff = useLastCallback(() => {
|
||||
if (canTakeOff) {
|
||||
setEmojiStatus({
|
||||
emojiStatus: { type: 'regular', documentId: DEFAULT_STATUS_ICON_ID },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handleFocusUpgraded = useLastCallback(() => {
|
||||
if (!savedGift?.upgradeMsgId || !renderingTargetPeer) return;
|
||||
const { upgradeMsgId } = savedGift;
|
||||
@ -271,37 +305,39 @@ const GiftInfoModal = ({
|
||||
})();
|
||||
|
||||
function getTitle() {
|
||||
if (gift?.type === 'starGiftUnique') return gift.title;
|
||||
if (isGiftUnique) return gift.title;
|
||||
if (!savedGift) return lang('GiftInfoSoldOutTitle');
|
||||
|
||||
return canUpdate ? lang('GiftInfoReceived') : lang('GiftInfoTitle');
|
||||
}
|
||||
|
||||
const isUniqueGift = gift.type === 'starGiftUnique';
|
||||
|
||||
const contextMenu = (
|
||||
const uniqueGiftContextMenu = (
|
||||
<DropdownMenu
|
||||
className="with-menu-transitions"
|
||||
trigger={SettingsMenuButton}
|
||||
positionX="right"
|
||||
>
|
||||
<MenuItem
|
||||
icon="link-badge"
|
||||
onClick={handleCopyLink}
|
||||
>
|
||||
<MenuItem icon="link-badge" onClick={handleCopyLink}>
|
||||
{lang('CopyLink')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon="forward"
|
||||
onClick={handleLinkShare}
|
||||
>
|
||||
<MenuItem icon="forward" onClick={handleLinkShare}>
|
||||
{lang('Share')}
|
||||
</MenuItem>
|
||||
{canUpdate && isUniqueGift && (
|
||||
{canUpdate && isGiftUnique && (
|
||||
<MenuItem icon="diamond" onClick={handleTransfer}>
|
||||
{lang('GiftInfoTransfer')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{canWear && (
|
||||
<MenuItem icon="crown-wear" onClick={handleWear}>
|
||||
{lang('GiftInfoWear')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{canTakeOff && (
|
||||
<MenuItem icon="crown-take-off" onClick={handleTakeOff}>
|
||||
{lang('GiftInfoTakeOff')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@ -319,11 +355,11 @@ const GiftInfoModal = ({
|
||||
>
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
{isOpen && contextMenu}
|
||||
{isOpen && uniqueGiftContextMenu}
|
||||
</div>
|
||||
);
|
||||
|
||||
const uniqueGiftHeader = isUniqueGift && (
|
||||
const uniqueGiftHeader = isGiftUnique && (
|
||||
<div className={buildClassName(styles.header, styles.uniqueGift)}>
|
||||
<UniqueGiftHeader
|
||||
backdropAttribute={giftAttributes!.backdrop!}
|
||||
@ -432,7 +468,7 @@ const GiftInfoModal = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (gift.type === 'starGiftUnique') {
|
||||
if (isGiftUnique) {
|
||||
const { ownerName, ownerAddress, ownerId } = gift;
|
||||
const {
|
||||
model, backdrop, pattern, originalDetails,
|
||||
@ -458,7 +494,7 @@ const GiftInfoModal = ({
|
||||
} else {
|
||||
tableData.push([
|
||||
lang('GiftInfoOwner'),
|
||||
ownerId ? { chatId: ownerId } : ownerName || '',
|
||||
ownerId ? { chatId: ownerId, withEmojiStatus: true } : ownerName || '',
|
||||
]);
|
||||
}
|
||||
|
||||
@ -590,14 +626,16 @@ const GiftInfoModal = ({
|
||||
);
|
||||
|
||||
return {
|
||||
modalHeader: isUniqueGift ? uniqueGiftModalHeader : undefined,
|
||||
header: isUniqueGift ? uniqueGiftHeader : regularHeader,
|
||||
modalHeader: isGiftUnique ? uniqueGiftModalHeader : undefined,
|
||||
header: isGiftUnique ? uniqueGiftHeader : regularHeader,
|
||||
tableData,
|
||||
footer,
|
||||
};
|
||||
}, [
|
||||
typeGift, savedGift, renderingTargetPeer, giftSticker, lang, canUpdate, canConvertDifference, isSender, oldLang,
|
||||
gift, giftAttributes, renderFooterButton, isTargetChat, SettingsMenuButton, isOpen,
|
||||
typeGift, savedGift, renderingTargetPeer, giftSticker, lang,
|
||||
canUpdate, canConvertDifference, isSender, oldLang,
|
||||
gift, giftAttributes, renderFooterButton, isTargetChat,
|
||||
SettingsMenuButton, isOpen, isGiftUnique, canWear, canTakeOff,
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -606,7 +644,7 @@ const GiftInfoModal = ({
|
||||
isOpen={isOpen}
|
||||
modalHeader={modalData?.modalHeader}
|
||||
header={modalData?.header}
|
||||
hasBackdrop={gift?.type === 'starGiftUnique'}
|
||||
hasBackdrop={isGiftUnique}
|
||||
tableData={modalData?.tableData}
|
||||
footer={modalData?.footer}
|
||||
className={styles.modal}
|
||||
@ -656,6 +694,9 @@ export default memo(withGlobal<OwnProps>(
|
||||
const targetPeer = modal?.peerId ? selectPeer(global, modal.peerId) : undefined;
|
||||
const chat = targetPeer && isApiPeerChat(targetPeer) ? targetPeer : undefined;
|
||||
const hasAdminRights = chat && getHasAdminRight(chat, 'postMessages');
|
||||
const currentUser = global.currentUserId ? selectUser(global, global.currentUserId) : undefined;
|
||||
const currentUserEmojiStatus = currentUser?.emojiStatus;
|
||||
const collectibleEmojiStatuses = global.collectibleEmojiStatuses?.statuses;
|
||||
|
||||
return {
|
||||
fromPeer,
|
||||
@ -663,6 +704,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
currentUserId: global.currentUserId,
|
||||
starGiftMaxConvertPeriod: global.appConfig?.starGiftMaxConvertPeriod,
|
||||
hasAdminRights,
|
||||
currentUserEmojiStatus,
|
||||
collectibleEmojiStatuses,
|
||||
};
|
||||
},
|
||||
)(GiftInfoModal));
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import type { FC } from '../../../../lib/teact/teact';
|
||||
import React from '../../../../lib/teact/teact';
|
||||
|
||||
import type { OwnProps } from './GiftStatusInfoModal';
|
||||
|
||||
import { Bundles } from '../../../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||
|
||||
const GiftStatusInfoModalAsync: FC<OwnProps> = (props) => {
|
||||
const { modal } = props;
|
||||
const GiftStatusInfoModal = useModuleLoader(Bundles.Stars, 'GiftStatusInfoModal', !modal);
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return GiftStatusInfoModal ? <GiftStatusInfoModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default GiftStatusInfoModalAsync;
|
||||
@ -0,0 +1,72 @@
|
||||
.header,
|
||||
.profileBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.profileBlock {
|
||||
position: relative;
|
||||
margin-bottom: 0.5rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.radialPattern {
|
||||
position: absolute;
|
||||
inset-inline-start: -1.5rem;
|
||||
height: calc(100% + 2rem);
|
||||
top: -3rem;
|
||||
width: calc(100% + 3rem);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.lockIcon {
|
||||
font-size: 1rem;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.userTitle {
|
||||
font-size: 1.25rem;
|
||||
color: white;
|
||||
margin-top: auto;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 0.875rem;
|
||||
text-align: center;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.userTitle, .status {
|
||||
margin-bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.giftTitle {
|
||||
font-weight: 500;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.infoDescription {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
}
|
||||
171
src/components/modals/gift/status/GiftStatusInfoModal.tsx
Normal file
171
src/components/modals/gift/status/GiftStatusInfoModal.tsx
Normal file
@ -0,0 +1,171 @@
|
||||
import React, { memo, useMemo } from '../../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../../global';
|
||||
|
||||
import type {
|
||||
ApiUser,
|
||||
} from '../../../../api/types';
|
||||
import type { TabState } from '../../../../global/types';
|
||||
|
||||
import { selectIsCurrentUserPremium, selectUser } from '../../../../global/selectors';
|
||||
import buildClassName from '../../../../util/buildClassName';
|
||||
import buildStyle from '../../../../util/buildStyle';
|
||||
|
||||
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||
import useLang from '../../../../hooks/useLang';
|
||||
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||
import useCustomEmoji from '../../../common/hooks/useCustomEmoji';
|
||||
|
||||
import Avatar from '../../../common/Avatar';
|
||||
import FullNameTitle from '../../../common/FullNameTitle';
|
||||
import Icon from '../../../common/icons/Icon';
|
||||
import RadialPatternBackground from '../../../common/profile/RadialPatternBackground';
|
||||
import Button from '../../../ui/Button';
|
||||
import TableAboutModal, { type TableAboutData } from '../../common/TableAboutModal';
|
||||
|
||||
import styles from './GiftStatusInfoModal.module.scss';
|
||||
|
||||
export type OwnProps = {
|
||||
modal: TabState['giftStatusInfoModal'];
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
currentUser: ApiUser;
|
||||
isCurrentUserPremium?: boolean;
|
||||
};
|
||||
|
||||
const GiftStatusInfoModal = ({
|
||||
modal,
|
||||
currentUser,
|
||||
isCurrentUserPremium,
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
closeGiftStatusInfoModal,
|
||||
setEmojiStatus,
|
||||
} = getActions();
|
||||
const lang = useLang();
|
||||
const isOpen = Boolean(modal);
|
||||
const renderingModal = useCurrentOrPrev(modal);
|
||||
|
||||
const { emojiStatus } = renderingModal || {};
|
||||
|
||||
const subtitleColor = emojiStatus?.textColor;
|
||||
|
||||
const patternIcon = useCustomEmoji(emojiStatus?.patternDocumentId);
|
||||
|
||||
const handleClose = useLastCallback(() => {
|
||||
closeGiftStatusInfoModal();
|
||||
});
|
||||
|
||||
const onWearClick = useLastCallback(() => {
|
||||
if (emojiStatus) {
|
||||
setEmojiStatus({ emojiStatus });
|
||||
}
|
||||
closeGiftStatusInfoModal();
|
||||
});
|
||||
|
||||
const radialPatternBackdrop = useMemo(() => {
|
||||
if (!emojiStatus || !isOpen) return undefined;
|
||||
|
||||
const backdropColors = [emojiStatus.centerColor, emojiStatus.edgeColor];
|
||||
const patternColor = emojiStatus.patternColor;
|
||||
|
||||
return (
|
||||
<RadialPatternBackground
|
||||
className={styles.radialPattern}
|
||||
backgroundColors={backdropColors}
|
||||
patternColor={patternColor}
|
||||
patternIcon={patternIcon.customEmoji}
|
||||
/>
|
||||
);
|
||||
}, [emojiStatus, isOpen, patternIcon]);
|
||||
|
||||
const mockPeerWithStatus = useMemo(() => {
|
||||
return {
|
||||
...currentUser,
|
||||
emojiStatus,
|
||||
} satisfies ApiUser;
|
||||
}, [currentUser, emojiStatus]);
|
||||
|
||||
const header = useMemo(() => {
|
||||
return (
|
||||
<div className={styles.header}>
|
||||
<div
|
||||
className={buildClassName(styles.profileBlock)}
|
||||
style={buildStyle(subtitleColor && `color: ${subtitleColor}`)}
|
||||
>
|
||||
|
||||
{radialPatternBackdrop}
|
||||
<Avatar peer={mockPeerWithStatus} size="jumbo" className={styles.avatar} />
|
||||
<FullNameTitle
|
||||
peer={mockPeerWithStatus}
|
||||
className={styles.userTitle}
|
||||
withEmojiStatus
|
||||
noFake
|
||||
noVerified
|
||||
statusSparklesColor={subtitleColor}
|
||||
/>
|
||||
<p className={styles.status} style={buildStyle(subtitleColor && `color: ${subtitleColor}`)}>
|
||||
{lang('Online')}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.titleContainer}>
|
||||
<div className={styles.giftTitle}>{
|
||||
lang('UniqueStatusWearTitle', {
|
||||
gift: mockPeerWithStatus?.emojiStatus?.title,
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div className={styles.infoDescription}>{
|
||||
lang('UniqueStatusBenefitsDescription')
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [subtitleColor, radialPatternBackdrop, mockPeerWithStatus, lang]);
|
||||
|
||||
const listItemData = [
|
||||
['radial-badge', lang('UniqueStatusBadgeBenefitTitle'), lang('UniqueStatusBadgeDescription')],
|
||||
['unique-profile', lang('UniqueStatusProfileDesignBenefitTitle'), lang('UniqueStatusProfileDesignDescription')],
|
||||
['proof-of-ownership', lang('UniqueStatusProofOfOwnershipBenefitTitle'),
|
||||
lang('UniqueStatusProofOfOwnershipDescription')],
|
||||
] satisfies TableAboutData;
|
||||
|
||||
const footer = useMemo(() => {
|
||||
if (!isOpen) return undefined;
|
||||
return (
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
size="smaller"
|
||||
onClick={onWearClick}
|
||||
>
|
||||
{lang('UniqueStatusWearButton')}
|
||||
{!isCurrentUserPremium && <Icon name="lock-badge" className={styles.lockIcon} />}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}, [lang, isCurrentUserPremium, isOpen]);
|
||||
|
||||
return (
|
||||
<TableAboutModal
|
||||
isOpen={isOpen}
|
||||
header={header}
|
||||
listItemData={listItemData}
|
||||
footer={footer}
|
||||
hasBackdrop
|
||||
onClose={handleClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const currentUser = selectUser(global, global.currentUserId!)!;
|
||||
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
||||
|
||||
return {
|
||||
currentUser,
|
||||
isCurrentUserPremium,
|
||||
};
|
||||
},
|
||||
)(GiftStatusInfoModal));
|
||||
@ -45,6 +45,7 @@ const SuggestedStatusModal = ({ modal, currentUser, bot }: OwnProps & StateProps
|
||||
return {
|
||||
...currentUser,
|
||||
emojiStatus: {
|
||||
type: 'regular',
|
||||
documentId: renderingModal.customEmojiId,
|
||||
},
|
||||
} satisfies ApiUser;
|
||||
@ -93,8 +94,7 @@ const SuggestedStatusModal = ({ modal, currentUser, bot }: OwnProps & StateProps
|
||||
|
||||
setEmojiStatus({
|
||||
referrerWebAppKey: renderingModal.webAppKey,
|
||||
emojiStatusId: renderingModal.customEmojiId,
|
||||
expires,
|
||||
emojiStatus: { type: 'regular', documentId: renderingModal.customEmojiId, until: expires },
|
||||
});
|
||||
closeSuggestedStatusModal();
|
||||
});
|
||||
|
||||
@ -86,6 +86,10 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.info .Transition {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.Transition {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@ -104,7 +104,6 @@
|
||||
text-align: initial;
|
||||
unicode-bidi: plaintext;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.other-usernames {
|
||||
@ -225,7 +224,7 @@
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.info-name-title {
|
||||
@ -236,10 +235,10 @@
|
||||
.info-row,
|
||||
.title,
|
||||
.subtitle {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.separator {
|
||||
@ -287,7 +286,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
|
||||
@ -219,6 +219,7 @@ export const EMOJI_SIZES = 7;
|
||||
export const TOP_SYMBOL_SET_ID = 'top';
|
||||
export const POPULAR_SYMBOL_SET_ID = 'popular';
|
||||
export const RECENT_SYMBOL_SET_ID = 'recent';
|
||||
export const COLLECTIBLE_STATUS_SET_ID = 'collectibleStatus';
|
||||
export const FAVORITE_SYMBOL_SET_ID = 'favorite';
|
||||
export const EFFECT_STICKERS_SET_ID = 'effectStickers';
|
||||
export const EFFECT_EMOJIS_SET_ID = 'effectEmojis';
|
||||
|
||||
@ -239,6 +239,31 @@ addActionHandler('loadDefaultStatusIcons', async (global): Promise<void> => {
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('loadUserCollectibleStatuses', async (global, actions): Promise<void> => {
|
||||
setGlobal(global);
|
||||
|
||||
const { hash } = global.collectibleEmojiStatuses || {};
|
||||
|
||||
const result = await callApi('fetchCollectibleEmojiStatuses', { hash });
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
|
||||
global = {
|
||||
...global,
|
||||
collectibleEmojiStatuses: {
|
||||
hash: result.hash,
|
||||
statuses: result.statuses,
|
||||
},
|
||||
};
|
||||
setGlobal(global);
|
||||
const documentIds = result.statuses.map(({ documentId }) => documentId);
|
||||
|
||||
actions.loadCustomEmojis({ ids: documentIds });
|
||||
});
|
||||
|
||||
addActionHandler('loadStickers', (global, actions, payload): ActionReturnType => {
|
||||
const { stickerSetInfo } = payload;
|
||||
const cachedSet = selectStickerSet(global, stickerSetInfo);
|
||||
|
||||
@ -389,7 +389,7 @@ addActionHandler('reportSpam', (global, actions, payload): ActionReturnType => {
|
||||
|
||||
addActionHandler('setEmojiStatus', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
emojiStatusId, referrerWebAppKey, expires, tabId = getCurrentTabId(),
|
||||
emojiStatus, referrerWebAppKey, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
||||
@ -411,7 +411,7 @@ addActionHandler('setEmojiStatus', async (global, actions, payload): Promise<voi
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await callApi('updateEmojiStatus', emojiStatusId, expires);
|
||||
const result = await callApi('updateEmojiStatus', emojiStatus);
|
||||
|
||||
if (referrerWebAppKey) {
|
||||
if (!result) {
|
||||
@ -439,7 +439,7 @@ addActionHandler('setEmojiStatus', async (global, actions, payload): Promise<voi
|
||||
message: {
|
||||
key: 'BotSuggestedStatusUpdated',
|
||||
},
|
||||
customEmojiIconId: emojiStatusId,
|
||||
customEmojiIconId: emojiStatus.documentId,
|
||||
tabId,
|
||||
});
|
||||
}
|
||||
|
||||
@ -269,6 +269,24 @@ addActionHandler('openGiftWithdrawModal', (global, actions, payload): ActionRetu
|
||||
|
||||
addTabStateResetterAction('closeGiftWithdrawModal', 'giftWithdrawModal');
|
||||
|
||||
addActionHandler('openGiftStatusInfoModal', (global, actions, payload): ActionReturnType => {
|
||||
const { emojiStatus, tabId = getCurrentTabId() } = payload || {};
|
||||
|
||||
return updateTabState(global, {
|
||||
giftStatusInfoModal: {
|
||||
emojiStatus,
|
||||
},
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('closeGiftStatusInfoModal', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
|
||||
return updateTabState(global, {
|
||||
giftStatusInfoModal: undefined,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('clearGiftWithdrawError', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
const tabState = selectTabState(global, tabId);
|
||||
|
||||
@ -50,6 +50,7 @@ import type {
|
||||
BotsPrivacyType,
|
||||
PrivacyVisibility,
|
||||
} from '../../api/types';
|
||||
import type { ApiEmojiStatusCollectible, ApiEmojiStatusType } from '../../api/types/users';
|
||||
import type { ApiCredentials } from '../../components/payment/PaymentModal';
|
||||
import type { FoldersActions } from '../../hooks/reducers/useFoldersReducer';
|
||||
import type { ReducerAction } from '../../hooks/useReducer';
|
||||
@ -1831,6 +1832,7 @@ export interface ActionPayloads {
|
||||
clearRecentCustomEmoji: undefined;
|
||||
loadFeaturedEmojiStickers: undefined;
|
||||
loadDefaultStatusIcons: undefined;
|
||||
loadUserCollectibleStatuses: undefined;
|
||||
loadRecentEmojiStatuses: undefined;
|
||||
|
||||
// Bots
|
||||
@ -2345,6 +2347,10 @@ export interface ActionPayloads {
|
||||
} & WithTabId;
|
||||
clearGiftWithdrawError: WithTabId | undefined;
|
||||
closeGiftWithdrawModal: WithTabId | undefined;
|
||||
openGiftStatusInfoModal: {
|
||||
emojiStatus: ApiEmojiStatusCollectible;
|
||||
} & WithTabId;
|
||||
closeGiftStatusInfoModal: WithTabId | undefined;
|
||||
processStarGiftWithdrawal: {
|
||||
gift: ApiInputSavedStarGift;
|
||||
password: string;
|
||||
@ -2379,8 +2385,7 @@ export interface ActionPayloads {
|
||||
closeStarsGiftModal: WithTabId | undefined;
|
||||
|
||||
setEmojiStatus: {
|
||||
emojiStatusId: string;
|
||||
expires?: number;
|
||||
emojiStatus: ApiEmojiStatusType;
|
||||
referrerWebAppKey?: string;
|
||||
} & WithTabId;
|
||||
openSuggestedStatusModal: {
|
||||
|
||||
@ -11,6 +11,7 @@ import type {
|
||||
ApiConfig,
|
||||
ApiCountry,
|
||||
ApiCountryCode,
|
||||
ApiEmojiStatusType,
|
||||
ApiGroupCall,
|
||||
ApiLanguage,
|
||||
ApiMessage,
|
||||
@ -361,6 +362,11 @@ export type GlobalState = {
|
||||
premiumGifts?: ApiStickerSet;
|
||||
emojiKeywords: Record<string, EmojiKeywords | undefined>;
|
||||
|
||||
collectibleEmojiStatuses?: {
|
||||
statuses: ApiEmojiStatusType[];
|
||||
hash?: string;
|
||||
};
|
||||
|
||||
gifs: {
|
||||
saved: {
|
||||
hash?: string;
|
||||
|
||||
@ -47,6 +47,7 @@ import type {
|
||||
ApiVideo,
|
||||
ApiWebPage,
|
||||
} from '../../api/types';
|
||||
import type { ApiEmojiStatusCollectible } from '../../api/types/users';
|
||||
import type { FoldersActions } from '../../hooks/reducers/useFoldersReducer';
|
||||
import type { ReducerAction } from '../../hooks/useReducer';
|
||||
import type {
|
||||
@ -737,6 +738,10 @@ export type TabState = {
|
||||
errorKey?: RegularLangFnParameters;
|
||||
};
|
||||
|
||||
giftStatusInfoModal?: {
|
||||
emojiStatus: ApiEmojiStatusCollectible;
|
||||
};
|
||||
|
||||
suggestedStatusModal?: {
|
||||
botId: string;
|
||||
webAppKey?: string;
|
||||
|
||||
@ -1462,6 +1462,7 @@ account.reorderUsernames#ef500eab order:Vector<string> = Bool;
|
||||
account.toggleUsername#58d6b376 username:string active:Bool = Bool;
|
||||
account.resolveBusinessChatLink#5492e5ee slug:string = account.ResolvedBusinessChatLinks;
|
||||
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
|
||||
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
||||
|
||||
@ -60,6 +60,7 @@
|
||||
"account.toggleUsername",
|
||||
"account.resolveBusinessChatLink",
|
||||
"account.toggleSponsoredMessages",
|
||||
"account.getCollectibleEmojiStatuses",
|
||||
"users.getUsers",
|
||||
"users.getFullUser",
|
||||
"contacts.getContacts",
|
||||
|
||||
@ -94,196 +94,201 @@ $icons-map: (
|
||||
"comments": "\f139",
|
||||
"copy-media": "\f13a",
|
||||
"copy": "\f13b",
|
||||
"darkmode": "\f13c",
|
||||
"data": "\f13d",
|
||||
"delete-filled": "\f13e",
|
||||
"delete-left": "\f13f",
|
||||
"delete-user": "\f140",
|
||||
"delete": "\f141",
|
||||
"diamond": "\f142",
|
||||
"document": "\f143",
|
||||
"double-badge": "\f144",
|
||||
"down": "\f145",
|
||||
"download": "\f146",
|
||||
"eats": "\f147",
|
||||
"edit": "\f148",
|
||||
"email": "\f149",
|
||||
"enter": "\f14a",
|
||||
"expand-modal": "\f14b",
|
||||
"expand": "\f14c",
|
||||
"eye-closed-outline": "\f14d",
|
||||
"eye-closed": "\f14e",
|
||||
"eye-outline": "\f14f",
|
||||
"eye": "\f150",
|
||||
"favorite-filled": "\f151",
|
||||
"favorite": "\f152",
|
||||
"file-badge": "\f153",
|
||||
"flag": "\f154",
|
||||
"folder-badge": "\f155",
|
||||
"folder": "\f156",
|
||||
"fontsize": "\f157",
|
||||
"forums": "\f158",
|
||||
"forward": "\f159",
|
||||
"fragment": "\f15a",
|
||||
"fullscreen": "\f15b",
|
||||
"gifs": "\f15c",
|
||||
"gift": "\f15d",
|
||||
"group-filled": "\f15e",
|
||||
"group": "\f15f",
|
||||
"grouped-disable": "\f160",
|
||||
"grouped": "\f161",
|
||||
"hand-stop": "\f162",
|
||||
"hashtag": "\f163",
|
||||
"heart-outline": "\f164",
|
||||
"heart": "\f165",
|
||||
"help": "\f166",
|
||||
"info-filled": "\f167",
|
||||
"info": "\f168",
|
||||
"install": "\f169",
|
||||
"italic": "\f16a",
|
||||
"key": "\f16b",
|
||||
"keyboard": "\f16c",
|
||||
"lamp": "\f16d",
|
||||
"language": "\f16e",
|
||||
"large-pause": "\f16f",
|
||||
"large-play": "\f170",
|
||||
"link-badge": "\f171",
|
||||
"link-broken": "\f172",
|
||||
"link": "\f173",
|
||||
"location": "\f174",
|
||||
"lock-badge": "\f175",
|
||||
"lock": "\f176",
|
||||
"logout": "\f177",
|
||||
"loop": "\f178",
|
||||
"mention": "\f179",
|
||||
"message-failed": "\f17a",
|
||||
"message-pending": "\f17b",
|
||||
"message-read": "\f17c",
|
||||
"message-succeeded": "\f17d",
|
||||
"message": "\f17e",
|
||||
"microphone-alt": "\f17f",
|
||||
"microphone": "\f180",
|
||||
"monospace": "\f181",
|
||||
"more-circle": "\f182",
|
||||
"more": "\f183",
|
||||
"move-caption-down": "\f184",
|
||||
"move-caption-up": "\f185",
|
||||
"mute": "\f186",
|
||||
"muted": "\f187",
|
||||
"my-notes": "\f188",
|
||||
"new-chat-filled": "\f189",
|
||||
"next": "\f18a",
|
||||
"nochannel": "\f18b",
|
||||
"noise-suppression": "\f18c",
|
||||
"non-contacts": "\f18d",
|
||||
"one-filled": "\f18e",
|
||||
"open-in-new-tab": "\f18f",
|
||||
"password-off": "\f190",
|
||||
"pause": "\f191",
|
||||
"permissions": "\f192",
|
||||
"phone-discard-outline": "\f193",
|
||||
"phone-discard": "\f194",
|
||||
"phone": "\f195",
|
||||
"photo": "\f196",
|
||||
"pin-badge": "\f197",
|
||||
"pin-list": "\f198",
|
||||
"pin": "\f199",
|
||||
"pinned-chat": "\f19a",
|
||||
"pinned-message": "\f19b",
|
||||
"pip": "\f19c",
|
||||
"play-story": "\f19d",
|
||||
"play": "\f19e",
|
||||
"poll": "\f19f",
|
||||
"previous": "\f1a0",
|
||||
"privacy-policy": "\f1a1",
|
||||
"quote-text": "\f1a2",
|
||||
"quote": "\f1a3",
|
||||
"readchats": "\f1a4",
|
||||
"recent": "\f1a5",
|
||||
"reload": "\f1a6",
|
||||
"remove-quote": "\f1a7",
|
||||
"remove": "\f1a8",
|
||||
"reopen-topic": "\f1a9",
|
||||
"replace": "\f1aa",
|
||||
"replies": "\f1ab",
|
||||
"reply-filled": "\f1ac",
|
||||
"reply": "\f1ad",
|
||||
"revenue-split": "\f1ae",
|
||||
"revote": "\f1af",
|
||||
"save-story": "\f1b0",
|
||||
"saved-messages": "\f1b1",
|
||||
"schedule": "\f1b2",
|
||||
"search": "\f1b3",
|
||||
"select": "\f1b4",
|
||||
"send-outline": "\f1b5",
|
||||
"send": "\f1b6",
|
||||
"settings-filled": "\f1b7",
|
||||
"settings": "\f1b8",
|
||||
"share-filled": "\f1b9",
|
||||
"share-screen-outlined": "\f1ba",
|
||||
"share-screen-stop": "\f1bb",
|
||||
"share-screen": "\f1bc",
|
||||
"show-message": "\f1bd",
|
||||
"sidebar": "\f1be",
|
||||
"skip-next": "\f1bf",
|
||||
"skip-previous": "\f1c0",
|
||||
"smallscreen": "\f1c1",
|
||||
"smile": "\f1c2",
|
||||
"sort": "\f1c3",
|
||||
"speaker-muted-story": "\f1c4",
|
||||
"speaker-outline": "\f1c5",
|
||||
"speaker-story": "\f1c6",
|
||||
"speaker": "\f1c7",
|
||||
"spoiler-disable": "\f1c8",
|
||||
"spoiler": "\f1c9",
|
||||
"sport": "\f1ca",
|
||||
"star": "\f1cb",
|
||||
"stars-lock": "\f1cc",
|
||||
"stats": "\f1cd",
|
||||
"stealth-future": "\f1ce",
|
||||
"stealth-past": "\f1cf",
|
||||
"stickers": "\f1d0",
|
||||
"stop-raising-hand": "\f1d1",
|
||||
"stop": "\f1d2",
|
||||
"story-caption": "\f1d3",
|
||||
"story-expired": "\f1d4",
|
||||
"story-priority": "\f1d5",
|
||||
"story-reply": "\f1d6",
|
||||
"strikethrough": "\f1d7",
|
||||
"tag-add": "\f1d8",
|
||||
"tag-crossed": "\f1d9",
|
||||
"tag-filter": "\f1da",
|
||||
"tag-name": "\f1db",
|
||||
"tag": "\f1dc",
|
||||
"timer": "\f1dd",
|
||||
"toncoin": "\f1de",
|
||||
"trade": "\f1df",
|
||||
"transcribe": "\f1e0",
|
||||
"truck": "\f1e1",
|
||||
"unarchive": "\f1e2",
|
||||
"underlined": "\f1e3",
|
||||
"unlock-badge": "\f1e4",
|
||||
"unlock": "\f1e5",
|
||||
"unmute": "\f1e6",
|
||||
"unpin": "\f1e7",
|
||||
"unread": "\f1e8",
|
||||
"up": "\f1e9",
|
||||
"user-filled": "\f1ea",
|
||||
"user-online": "\f1eb",
|
||||
"user": "\f1ec",
|
||||
"video-outlined": "\f1ed",
|
||||
"video-stop": "\f1ee",
|
||||
"video": "\f1ef",
|
||||
"view-once": "\f1f0",
|
||||
"voice-chat": "\f1f1",
|
||||
"volume-1": "\f1f2",
|
||||
"volume-2": "\f1f3",
|
||||
"volume-3": "\f1f4",
|
||||
"web": "\f1f5",
|
||||
"webapp": "\f1f6",
|
||||
"word-wrap": "\f1f7",
|
||||
"zoom-in": "\f1f8",
|
||||
"zoom-out": "\f1f9",
|
||||
"crown-take-off": "\f13c",
|
||||
"crown-wear": "\f13d",
|
||||
"darkmode": "\f13e",
|
||||
"data": "\f13f",
|
||||
"delete-filled": "\f140",
|
||||
"delete-left": "\f141",
|
||||
"delete-user": "\f142",
|
||||
"delete": "\f143",
|
||||
"diamond": "\f144",
|
||||
"document": "\f145",
|
||||
"double-badge": "\f146",
|
||||
"down": "\f147",
|
||||
"download": "\f148",
|
||||
"eats": "\f149",
|
||||
"edit": "\f14a",
|
||||
"email": "\f14b",
|
||||
"enter": "\f14c",
|
||||
"expand-modal": "\f14d",
|
||||
"expand": "\f14e",
|
||||
"eye-closed-outline": "\f14f",
|
||||
"eye-closed": "\f150",
|
||||
"eye-outline": "\f151",
|
||||
"eye": "\f152",
|
||||
"favorite-filled": "\f153",
|
||||
"favorite": "\f154",
|
||||
"file-badge": "\f155",
|
||||
"flag": "\f156",
|
||||
"folder-badge": "\f157",
|
||||
"folder": "\f158",
|
||||
"fontsize": "\f159",
|
||||
"forums": "\f15a",
|
||||
"forward": "\f15b",
|
||||
"fragment": "\f15c",
|
||||
"fullscreen": "\f15d",
|
||||
"gifs": "\f15e",
|
||||
"gift": "\f15f",
|
||||
"group-filled": "\f160",
|
||||
"group": "\f161",
|
||||
"grouped-disable": "\f162",
|
||||
"grouped": "\f163",
|
||||
"hand-stop": "\f164",
|
||||
"hashtag": "\f165",
|
||||
"heart-outline": "\f166",
|
||||
"heart": "\f167",
|
||||
"help": "\f168",
|
||||
"info-filled": "\f169",
|
||||
"info": "\f16a",
|
||||
"install": "\f16b",
|
||||
"italic": "\f16c",
|
||||
"key": "\f16d",
|
||||
"keyboard": "\f16e",
|
||||
"lamp": "\f16f",
|
||||
"language": "\f170",
|
||||
"large-pause": "\f171",
|
||||
"large-play": "\f172",
|
||||
"link-badge": "\f173",
|
||||
"link-broken": "\f174",
|
||||
"link": "\f175",
|
||||
"location": "\f176",
|
||||
"lock-badge": "\f177",
|
||||
"lock": "\f178",
|
||||
"logout": "\f179",
|
||||
"loop": "\f17a",
|
||||
"mention": "\f17b",
|
||||
"message-failed": "\f17c",
|
||||
"message-pending": "\f17d",
|
||||
"message-read": "\f17e",
|
||||
"message-succeeded": "\f17f",
|
||||
"message": "\f180",
|
||||
"microphone-alt": "\f181",
|
||||
"microphone": "\f182",
|
||||
"monospace": "\f183",
|
||||
"more-circle": "\f184",
|
||||
"more": "\f185",
|
||||
"move-caption-down": "\f186",
|
||||
"move-caption-up": "\f187",
|
||||
"mute": "\f188",
|
||||
"muted": "\f189",
|
||||
"my-notes": "\f18a",
|
||||
"new-chat-filled": "\f18b",
|
||||
"next": "\f18c",
|
||||
"nochannel": "\f18d",
|
||||
"noise-suppression": "\f18e",
|
||||
"non-contacts": "\f18f",
|
||||
"one-filled": "\f190",
|
||||
"open-in-new-tab": "\f191",
|
||||
"password-off": "\f192",
|
||||
"pause": "\f193",
|
||||
"permissions": "\f194",
|
||||
"phone-discard-outline": "\f195",
|
||||
"phone-discard": "\f196",
|
||||
"phone": "\f197",
|
||||
"photo": "\f198",
|
||||
"pin-badge": "\f199",
|
||||
"pin-list": "\f19a",
|
||||
"pin": "\f19b",
|
||||
"pinned-chat": "\f19c",
|
||||
"pinned-message": "\f19d",
|
||||
"pip": "\f19e",
|
||||
"play-story": "\f19f",
|
||||
"play": "\f1a0",
|
||||
"poll": "\f1a1",
|
||||
"previous": "\f1a2",
|
||||
"privacy-policy": "\f1a3",
|
||||
"proof-of-ownership": "\f1a4",
|
||||
"quote-text": "\f1a5",
|
||||
"quote": "\f1a6",
|
||||
"radial-badge": "\f1a7",
|
||||
"readchats": "\f1a8",
|
||||
"recent": "\f1a9",
|
||||
"reload": "\f1aa",
|
||||
"remove-quote": "\f1ab",
|
||||
"remove": "\f1ac",
|
||||
"reopen-topic": "\f1ad",
|
||||
"replace": "\f1ae",
|
||||
"replies": "\f1af",
|
||||
"reply-filled": "\f1b0",
|
||||
"reply": "\f1b1",
|
||||
"revenue-split": "\f1b2",
|
||||
"revote": "\f1b3",
|
||||
"save-story": "\f1b4",
|
||||
"saved-messages": "\f1b5",
|
||||
"schedule": "\f1b6",
|
||||
"search": "\f1b7",
|
||||
"select": "\f1b8",
|
||||
"send-outline": "\f1b9",
|
||||
"send": "\f1ba",
|
||||
"settings-filled": "\f1bb",
|
||||
"settings": "\f1bc",
|
||||
"share-filled": "\f1bd",
|
||||
"share-screen-outlined": "\f1be",
|
||||
"share-screen-stop": "\f1bf",
|
||||
"share-screen": "\f1c0",
|
||||
"show-message": "\f1c1",
|
||||
"sidebar": "\f1c2",
|
||||
"skip-next": "\f1c3",
|
||||
"skip-previous": "\f1c4",
|
||||
"smallscreen": "\f1c5",
|
||||
"smile": "\f1c6",
|
||||
"sort": "\f1c7",
|
||||
"speaker-muted-story": "\f1c8",
|
||||
"speaker-outline": "\f1c9",
|
||||
"speaker-story": "\f1ca",
|
||||
"speaker": "\f1cb",
|
||||
"spoiler-disable": "\f1cc",
|
||||
"spoiler": "\f1cd",
|
||||
"sport": "\f1ce",
|
||||
"star": "\f1cf",
|
||||
"stars-lock": "\f1d0",
|
||||
"stats": "\f1d1",
|
||||
"stealth-future": "\f1d2",
|
||||
"stealth-past": "\f1d3",
|
||||
"stickers": "\f1d4",
|
||||
"stop-raising-hand": "\f1d5",
|
||||
"stop": "\f1d6",
|
||||
"story-caption": "\f1d7",
|
||||
"story-expired": "\f1d8",
|
||||
"story-priority": "\f1d9",
|
||||
"story-reply": "\f1da",
|
||||
"strikethrough": "\f1db",
|
||||
"tag-add": "\f1dc",
|
||||
"tag-crossed": "\f1dd",
|
||||
"tag-filter": "\f1de",
|
||||
"tag-name": "\f1df",
|
||||
"tag": "\f1e0",
|
||||
"timer": "\f1e1",
|
||||
"toncoin": "\f1e2",
|
||||
"trade": "\f1e3",
|
||||
"transcribe": "\f1e4",
|
||||
"truck": "\f1e5",
|
||||
"unarchive": "\f1e6",
|
||||
"underlined": "\f1e7",
|
||||
"unique-profile": "\f1e8",
|
||||
"unlock-badge": "\f1e9",
|
||||
"unlock": "\f1ea",
|
||||
"unmute": "\f1eb",
|
||||
"unpin": "\f1ec",
|
||||
"unread": "\f1ed",
|
||||
"up": "\f1ee",
|
||||
"user-filled": "\f1ef",
|
||||
"user-online": "\f1f0",
|
||||
"user": "\f1f1",
|
||||
"video-outlined": "\f1f2",
|
||||
"video-stop": "\f1f3",
|
||||
"video": "\f1f4",
|
||||
"view-once": "\f1f5",
|
||||
"voice-chat": "\f1f6",
|
||||
"volume-1": "\f1f7",
|
||||
"volume-2": "\f1f8",
|
||||
"volume-3": "\f1f9",
|
||||
"web": "\f1fa",
|
||||
"webapp": "\f1fb",
|
||||
"word-wrap": "\f1fc",
|
||||
"zoom-in": "\f1fd",
|
||||
"zoom-out": "\f1fe",
|
||||
);
|
||||
|
||||
.icon-active-sessions::before {
|
||||
@ -463,6 +468,12 @@ $icons-map: (
|
||||
.icon-copy::before {
|
||||
content: map.get($icons-map, "copy");
|
||||
}
|
||||
.icon-crown-take-off::before {
|
||||
content: map.get($icons-map, "crown-take-off");
|
||||
}
|
||||
.icon-crown-wear::before {
|
||||
content: map.get($icons-map, "crown-wear");
|
||||
}
|
||||
.icon-darkmode::before {
|
||||
content: map.get($icons-map, "darkmode");
|
||||
}
|
||||
@ -769,12 +780,18 @@ $icons-map: (
|
||||
.icon-privacy-policy::before {
|
||||
content: map.get($icons-map, "privacy-policy");
|
||||
}
|
||||
.icon-proof-of-ownership::before {
|
||||
content: map.get($icons-map, "proof-of-ownership");
|
||||
}
|
||||
.icon-quote-text::before {
|
||||
content: map.get($icons-map, "quote-text");
|
||||
}
|
||||
.icon-quote::before {
|
||||
content: map.get($icons-map, "quote");
|
||||
}
|
||||
.icon-radial-badge::before {
|
||||
content: map.get($icons-map, "radial-badge");
|
||||
}
|
||||
.icon-readchats::before {
|
||||
content: map.get($icons-map, "readchats");
|
||||
}
|
||||
@ -967,6 +984,9 @@ $icons-map: (
|
||||
.icon-underlined::before {
|
||||
content: map.get($icons-map, "underlined");
|
||||
}
|
||||
.icon-unique-profile::before {
|
||||
content: map.get($icons-map, "unique-profile");
|
||||
}
|
||||
.icon-unlock-badge::before {
|
||||
content: map.get($icons-map, "unlock-badge");
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -58,6 +58,8 @@ export type FontIconName =
|
||||
| 'comments'
|
||||
| 'copy-media'
|
||||
| 'copy'
|
||||
| 'crown-take-off'
|
||||
| 'crown-wear'
|
||||
| 'darkmode'
|
||||
| 'data'
|
||||
| 'delete-filled'
|
||||
@ -160,8 +162,10 @@ export type FontIconName =
|
||||
| 'poll'
|
||||
| 'previous'
|
||||
| 'privacy-policy'
|
||||
| 'proof-of-ownership'
|
||||
| 'quote-text'
|
||||
| 'quote'
|
||||
| 'radial-badge'
|
||||
| 'readchats'
|
||||
| 'recent'
|
||||
| 'reload'
|
||||
@ -226,6 +230,7 @@ export type FontIconName =
|
||||
| 'truck'
|
||||
| 'unarchive'
|
||||
| 'underlined'
|
||||
| 'unique-profile'
|
||||
| 'unlock-badge'
|
||||
| 'unlock'
|
||||
| 'unmute'
|
||||
|
||||
15
src/types/language.d.ts
vendored
15
src/types/language.d.ts
vendored
@ -1197,6 +1197,9 @@ export interface LangPair {
|
||||
'GiftInfoViewUpgraded': undefined;
|
||||
'GiftInfoUpgradeBadge': undefined;
|
||||
'GiftInfoUpgradeForFree': undefined;
|
||||
'GiftInfoWithdraw': undefined;
|
||||
'GiftInfoWear': undefined;
|
||||
'GiftInfoTakeOff': undefined;
|
||||
'GiftInfoTransfer': undefined;
|
||||
'GiftTransferTitle': undefined;
|
||||
'GiftTransferTON': undefined;
|
||||
@ -1321,6 +1324,15 @@ export interface LangPair {
|
||||
'CheckPasswordTitle': undefined;
|
||||
'CheckPasswordPlaceholder': undefined;
|
||||
'CheckPasswordDescription': undefined;
|
||||
'UniqueStatusBenefitsDescription': undefined;
|
||||
'UniqueStatusBadgeBenefitTitle': undefined;
|
||||
'UniqueStatusBadgeDescription': undefined;
|
||||
'UniqueStatusProfileDesignBenefitTitle': undefined;
|
||||
'UniqueStatusProfileDesignDescription': undefined;
|
||||
'UniqueStatusProofOfOwnershipBenefitTitle': undefined;
|
||||
'UniqueStatusProofOfOwnershipDescription': undefined;
|
||||
'UniqueStatusWearButton': undefined;
|
||||
'CollectibleStatusesCategory': undefined;
|
||||
}
|
||||
|
||||
export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
||||
@ -1835,6 +1847,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
||||
'MoreSimilarBotsText': {
|
||||
'count': V;
|
||||
};
|
||||
'UniqueStatusWearTitle': {
|
||||
'gift': V;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LangPairPlural {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user