Star Gift: Support craft (#6713)
107
CLAUDE.md
@ -420,3 +420,110 @@ lang('MarkdownKey', undefined, { withNodes: true, withMarkdown: true });
|
|||||||
|
|
||||||
**7. Beyond React**
|
**7. Beyond React**
|
||||||
Use `getTranslationFn()` to grab the same `lang` function in non-component code. Discouraged, use object syntax.
|
Use `getTranslationFn()` to grab the same `lang` function in non-component code. Discouraged, use object syntax.
|
||||||
|
|
||||||
|
# ⚠️ IMPORTANT: Fasterdom & Rendering Phases
|
||||||
|
|
||||||
|
## Rendering Cycle
|
||||||
|
|
||||||
|
```
|
||||||
|
--- frame start ---
|
||||||
|
1. effects
|
||||||
|
2. requested measures (DOM reads)
|
||||||
|
3. render JSX → DOM
|
||||||
|
4. layout effects
|
||||||
|
5. requested mutations (DOM writes)
|
||||||
|
6. forced reflow measure (avoid!)
|
||||||
|
7. forced reflow mutate (avoid!)
|
||||||
|
--- frame end ---
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phase Rules
|
||||||
|
|
||||||
|
| Hook/Context | Can READ (measure) | Can WRITE (mutate) |
|
||||||
|
|--------------|-------------------|-------------------|
|
||||||
|
| `useLayoutEffect` | ❌ NO | ✅ YES |
|
||||||
|
| `useLayout` (deprecated) | ✅ YES | ❌ NO |
|
||||||
|
| Event handlers (default) | ✅ YES | ❌ NO (use `requestMutation`) |
|
||||||
|
| `requestMeasure` callback | ✅ YES | ❌ NO |
|
||||||
|
| `requestMutation` callback | ❌ NO | ✅ YES |
|
||||||
|
|
||||||
|
## Usage Patterns
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ CORRECT: Read in measure phase, write in mutation phase
|
||||||
|
requestMeasure(() => {
|
||||||
|
const width = element.offsetWidth; // READ
|
||||||
|
|
||||||
|
requestMutation(() => {
|
||||||
|
element.style.width = `${width * 2}px`; // WRITE
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ❌ WRONG: Alternating reads/writes causes layout thrashing
|
||||||
|
const width = element.offsetWidth; // READ → reflow
|
||||||
|
element.style.width = `${width * 2}px`; // WRITE → reflow
|
||||||
|
const height = element.offsetHeight; // READ → reflow again!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals: State Without Re-renders
|
||||||
|
|
||||||
|
Signals deliver updates **without causing component renders**. Use for frequently-updated values.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Create signal
|
||||||
|
const [getValue, setValue] = createSignal(initialValue);
|
||||||
|
|
||||||
|
// Get value
|
||||||
|
getValue();
|
||||||
|
|
||||||
|
// Set value (notifies subscribers, NO re-render)
|
||||||
|
setValue(newValue);
|
||||||
|
|
||||||
|
// Subscribe to changes
|
||||||
|
getValue.subscribe(() => { /* react to change */ });
|
||||||
|
```
|
||||||
|
|
||||||
|
**Signal Hooks:**
|
||||||
|
- `useSignal()` – Create signal tied to component
|
||||||
|
- `useDerivedSignal()` – Derive new signal from other signals/variables
|
||||||
|
- `useDerivedState()` – Convert signal to render variable (triggers re-render)
|
||||||
|
- `useStateRef()` – Access current value without it being a dependency
|
||||||
|
|
||||||
|
**When to use signals:**
|
||||||
|
- Typing text, caret position
|
||||||
|
- Animation state tracking
|
||||||
|
- Values that change frequently but don't need re-render
|
||||||
|
- Cross-component communication without prop drilling
|
||||||
|
|
||||||
|
## Key Optimization Hooks
|
||||||
|
|
||||||
|
| Hook | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `useLastCallback` | Stable callback reference, always latest scope |
|
||||||
|
| `useStateRef` | Access state without triggering effects |
|
||||||
|
| `useLayoutEffectWithPrevDeps` | Synchronous effect with previous values |
|
||||||
|
| `useSyncEffect` | Effect that runs during render (not RAF) |
|
||||||
|
| `useResizeObserver` | Efficient element size observation |
|
||||||
|
| `useIntersectionObserver` | Viewport visibility tracking |
|
||||||
|
|
||||||
|
## Heavy Animation Handling
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Mark animation start (pauses non-critical updates)
|
||||||
|
const endAnimation = beginHeavyAnimation(duration);
|
||||||
|
|
||||||
|
// Run code only when fully idle (no animations + browser idle)
|
||||||
|
onFullyIdle(() => {
|
||||||
|
// Safe for heavy computations
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Checklist
|
||||||
|
|
||||||
|
1. **Animations first** – Evaluate if code negatively impacts animations
|
||||||
|
2. **Simplify algorithms** – Move complex ones to `onFullyIdle`
|
||||||
|
3. **No loops in selectors** – Avoid iterations in `withGlobal` selectors
|
||||||
|
4. **Minimize re-renders** – Especially in `Message`, `Chat`, `Sticker`, etc.
|
||||||
|
5. **Understand effect timing** – `useEffect` vs `useLayoutEffect`
|
||||||
|
6. **Prefer signals** – When you need effects only, not renders
|
||||||
|
7. **Use `requestForcedReflow`** – Only as last resort for sync measure+mutate
|
||||||
|
|||||||
@ -100,6 +100,7 @@ export interface GramJsAppConfig extends LimitsConfig {
|
|||||||
stars_stargift_resale_amount_max?: number;
|
stars_stargift_resale_amount_max?: number;
|
||||||
stars_stargift_resale_amount_min?: number;
|
stars_stargift_resale_amount_min?: number;
|
||||||
stars_stargift_resale_commission_permille?: number;
|
stars_stargift_resale_commission_permille?: number;
|
||||||
|
stargifts_craft_attribute_permilles?: number[];
|
||||||
ton_stargift_resale_amount_min?: number;
|
ton_stargift_resale_amount_min?: number;
|
||||||
ton_stargift_resale_amount_max?: number;
|
ton_stargift_resale_amount_max?: number;
|
||||||
ton_stargift_resale_commission_permille?: number;
|
ton_stargift_resale_commission_permille?: number;
|
||||||
@ -239,6 +240,7 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
|
|||||||
starsStargiftResaleAmountMin: appConfig.stars_stargift_resale_amount_min,
|
starsStargiftResaleAmountMin: appConfig.stars_stargift_resale_amount_min,
|
||||||
starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max,
|
starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max,
|
||||||
starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille,
|
starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille,
|
||||||
|
stargiftsCraftAttributePermilles: appConfig.stargifts_craft_attribute_permilles,
|
||||||
tonStargiftResaleAmountMin: appConfig.ton_stargift_resale_amount_min,
|
tonStargiftResaleAmountMin: appConfig.ton_stargift_resale_amount_min,
|
||||||
tonStargiftResaleAmountMax: appConfig.ton_stargift_resale_amount_max,
|
tonStargiftResaleAmountMax: appConfig.ton_stargift_resale_amount_max,
|
||||||
tonStargiftResaleCommissionPermille: appConfig.ton_stargift_resale_commission_permille,
|
tonStargiftResaleCommissionPermille: appConfig.ton_stargift_resale_commission_permille,
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
|||||||
const {
|
const {
|
||||||
id, num, ownerId, ownerName, title, attributes, availabilityIssued, availabilityTotal, slug, ownerAddress,
|
id, num, ownerId, ownerName, title, attributes, availabilityIssued, availabilityTotal, slug, ownerAddress,
|
||||||
giftAddress, resellAmount, releasedBy, resaleTonOnly, requirePremium, valueCurrency, valueAmount, giftId,
|
giftAddress, resellAmount, releasedBy, resaleTonOnly, requirePremium, valueCurrency, valueAmount, giftId,
|
||||||
valueUsdAmount, burned, crafted,
|
valueUsdAmount, burned, crafted, craftChancePermille,
|
||||||
} = starGift;
|
} = starGift;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -63,6 +63,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
|||||||
offerMinStars: starGift.offerMinStars,
|
offerMinStars: starGift.offerMinStars,
|
||||||
isBurned: burned,
|
isBurned: burned,
|
||||||
isCrafted: crafted,
|
isCrafted: crafted,
|
||||||
|
craftChancePermille,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ export function buildApiSavedStarGift(userStarGift: GramJs.SavedStarGift, peerId
|
|||||||
const {
|
const {
|
||||||
gift, date, convertStars, fromId, message, msgId, nameHidden, unsaved, refunded, upgradeStars, transferStars,
|
gift, date, convertStars, fromId, message, msgId, nameHidden, unsaved, refunded, upgradeStars, transferStars,
|
||||||
canUpgrade, savedId, canExportAt, pinnedToTop, canResellAt, canTransferAt, prepaidUpgradeHash,
|
canUpgrade, savedId, canExportAt, pinnedToTop, canResellAt, canTransferAt, prepaidUpgradeHash,
|
||||||
dropOriginalDetailsStars,
|
dropOriginalDetailsStars, canCraftAt,
|
||||||
} = userStarGift;
|
} = userStarGift;
|
||||||
|
|
||||||
const inputGift: ApiInputSavedStarGift | undefined = savedId && peerId
|
const inputGift: ApiInputSavedStarGift | undefined = savedId && peerId
|
||||||
@ -230,6 +231,7 @@ export function buildApiSavedStarGift(userStarGift: GramJs.SavedStarGift, peerId
|
|||||||
isPinned: pinnedToTop,
|
isPinned: pinnedToTop,
|
||||||
dropOriginalDetailsStars: dropOriginalDetailsStars !== undefined ? toJSNumber(dropOriginalDetailsStars) : undefined,
|
dropOriginalDetailsStars: dropOriginalDetailsStars !== undefined ? toJSNumber(dropOriginalDetailsStars) : undefined,
|
||||||
prepaidUpgradeHash,
|
prepaidUpgradeHash,
|
||||||
|
canCraftAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -426,7 +426,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
|
|||||||
if (action instanceof GramJs.MessageActionStarGiftUnique) {
|
if (action instanceof GramJs.MessageActionStarGiftUnique) {
|
||||||
const {
|
const {
|
||||||
upgrade, transferred, saved, refunded, gift, canExportAt, transferStars, fromId, peer, savedId,
|
upgrade, transferred, saved, refunded, gift, canExportAt, transferStars, fromId, peer, savedId,
|
||||||
resaleAmount, prepaidUpgrade, dropOriginalDetailsStars, fromOffer,
|
resaleAmount, prepaidUpgrade, dropOriginalDetailsStars, fromOffer, canCraftAt,
|
||||||
} = action;
|
} = action;
|
||||||
|
|
||||||
const starGift = buildApiStarGift(gift);
|
const starGift = buildApiStarGift(gift);
|
||||||
@ -451,6 +451,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
|
|||||||
dropOriginalDetailsStars: dropOriginalDetailsStars !== undefined
|
dropOriginalDetailsStars: dropOriginalDetailsStars !== undefined
|
||||||
? toJSNumber(dropOriginalDetailsStars)
|
? toJSNumber(dropOriginalDetailsStars)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
canCraftAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (action instanceof GramJs.MessageActionPaidMessagesPrice) {
|
if (action instanceof GramJs.MessageActionPaidMessagesPrice) {
|
||||||
|
|||||||
@ -105,12 +105,14 @@ export async function fetchResaleGifts({
|
|||||||
limit = DEFAULT_PRIMITIVES.INT,
|
limit = DEFAULT_PRIMITIVES.INT,
|
||||||
attributesHash,
|
attributesHash,
|
||||||
filter,
|
filter,
|
||||||
|
forCraft,
|
||||||
}: {
|
}: {
|
||||||
giftId: string;
|
giftId: string;
|
||||||
offset?: string;
|
offset?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
attributesHash?: string;
|
attributesHash?: string;
|
||||||
filter?: ResaleGiftsFilterOptions;
|
filter?: ResaleGiftsFilterOptions;
|
||||||
|
forCraft?: boolean;
|
||||||
}) {
|
}) {
|
||||||
type GetResaleStarGifts = ConstructorParameters<typeof GramJs.payments.GetResaleStarGifts>[0];
|
type GetResaleStarGifts = ConstructorParameters<typeof GramJs.payments.GetResaleStarGifts>[0];
|
||||||
|
|
||||||
@ -126,6 +128,7 @@ export async function fetchResaleGifts({
|
|||||||
limit,
|
limit,
|
||||||
attributesHash: attributesHash ? BigInt(attributesHash) : DEFAULT_PRIMITIVES.BIGINT,
|
attributesHash: attributesHash ? BigInt(attributesHash) : DEFAULT_PRIMITIVES.BIGINT,
|
||||||
attributes: buildInputResaleGiftsAttributes(attributes),
|
attributes: buildInputResaleGiftsAttributes(attributes),
|
||||||
|
forCraft: forCraft || undefined,
|
||||||
...(filter && {
|
...(filter && {
|
||||||
sortByPrice: filter.sortType === 'byPrice' || undefined,
|
sortByPrice: filter.sortType === 'byPrice' || undefined,
|
||||||
sortByNum: filter.sortType === 'byNumber' || undefined,
|
sortByNum: filter.sortType === 'byNumber' || undefined,
|
||||||
@ -631,6 +634,54 @@ export function resolveStarGiftOffer({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchCraftStarGifts({
|
||||||
|
giftId,
|
||||||
|
peerId,
|
||||||
|
offset = DEFAULT_PRIMITIVES.STRING,
|
||||||
|
limit = DEFAULT_PRIMITIVES.INT,
|
||||||
|
}: {
|
||||||
|
giftId: string;
|
||||||
|
peerId: string;
|
||||||
|
offset?: string;
|
||||||
|
limit?: number;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.payments.GetCraftStarGifts({
|
||||||
|
giftId: BigInt(giftId),
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
gifts: result.gifts.map((g) => buildApiSavedStarGift(g, peerId)),
|
||||||
|
nextOffset: result.nextOffset,
|
||||||
|
count: result.count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function craftStarGift({
|
||||||
|
inputSavedGifts,
|
||||||
|
}: {
|
||||||
|
inputSavedGifts: ApiRequestInputSavedStarGift[];
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await invokeRequest(new GramJs.payments.CraftStarGift({
|
||||||
|
stargift: inputSavedGifts.map(buildInputSavedStarGift),
|
||||||
|
}), {
|
||||||
|
shouldThrow: true,
|
||||||
|
});
|
||||||
|
return undefined;
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof RPCError) {
|
||||||
|
return { error: err.errorMessage };
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchStarGiftUpgradeAttributes({
|
export async function fetchStarGiftUpgradeAttributes({
|
||||||
giftId,
|
giftId,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@ -1136,6 +1136,10 @@ export function updater(update: Update) {
|
|||||||
giftId: update.giftId.toString(),
|
giftId: update.giftId.toString(),
|
||||||
userState: buildApiStarGiftAuctionUserState(update.userState),
|
userState: buildApiStarGiftAuctionUserState(update.userState),
|
||||||
});
|
});
|
||||||
|
} else if (update instanceof GramJs.UpdateStarGiftCraftFail) {
|
||||||
|
sendApiUpdate({
|
||||||
|
'@type': 'updateStarGiftCraftFail',
|
||||||
|
});
|
||||||
} else if (update instanceof GramJs.UpdatePaidReactionPrivacy) {
|
} else if (update instanceof GramJs.UpdatePaidReactionPrivacy) {
|
||||||
sendApiUpdate({
|
sendApiUpdate({
|
||||||
'@type': 'updatePaidReactionPrivacy',
|
'@type': 'updatePaidReactionPrivacy',
|
||||||
|
|||||||
@ -274,6 +274,7 @@ export interface ApiMessageActionStarGiftUnique extends ActionMediaType {
|
|||||||
savedId?: string;
|
savedId?: string;
|
||||||
resaleAmount?: ApiTypeCurrencyAmount;
|
resaleAmount?: ApiTypeCurrencyAmount;
|
||||||
dropOriginalDetailsStars?: number;
|
dropOriginalDetailsStars?: number;
|
||||||
|
canCraftAt?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiMessageActionChannelJoined extends ActionMediaType {
|
export interface ApiMessageActionChannelJoined extends ActionMediaType {
|
||||||
|
|||||||
@ -265,6 +265,7 @@ export interface ApiAppConfig {
|
|||||||
starsStargiftResaleAmountMin?: number;
|
starsStargiftResaleAmountMin?: number;
|
||||||
starsStargiftResaleAmountMax?: number;
|
starsStargiftResaleAmountMax?: number;
|
||||||
starsStargiftResaleCommissionPermille?: number;
|
starsStargiftResaleCommissionPermille?: number;
|
||||||
|
stargiftsCraftAttributePermilles?: number[];
|
||||||
starsSuggestedPostAmountMax: number;
|
starsSuggestedPostAmountMax: number;
|
||||||
starsSuggestedPostAmountMin: number;
|
starsSuggestedPostAmountMin: number;
|
||||||
starsSuggestedPostCommissionPermille: number;
|
starsSuggestedPostCommissionPermille: number;
|
||||||
|
|||||||
@ -63,6 +63,7 @@ export interface ApiStarGiftUnique {
|
|||||||
offerMinStars?: number;
|
offerMinStars?: number;
|
||||||
isBurned?: true;
|
isBurned?: true;
|
||||||
isCrafted?: true;
|
isCrafted?: true;
|
||||||
|
craftChancePermille?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiStarGift = ApiStarGiftRegular | ApiStarGiftUnique;
|
export type ApiStarGift = ApiStarGiftRegular | ApiStarGiftUnique;
|
||||||
@ -149,6 +150,7 @@ export interface ApiSavedStarGift {
|
|||||||
upgradeMsgId?: number; // Local field, used for Action Message
|
upgradeMsgId?: number; // Local field, used for Action Message
|
||||||
localTag?: number; // Local field, used for key in list
|
localTag?: number; // Local field, used for key in list
|
||||||
dropOriginalDetailsStars?: number;
|
dropOriginalDetailsStars?: number;
|
||||||
|
canCraftAt?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StarGiftAttributeIdModel = {
|
export type StarGiftAttributeIdModel = {
|
||||||
|
|||||||
@ -861,6 +861,10 @@ export type ApiUpdateStarGiftAuctionUserState = {
|
|||||||
userState: ApiStarGiftAuctionUserState;
|
userState: ApiStarGiftAuctionUserState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiUpdateStarGiftCraftFail = {
|
||||||
|
'@type': 'updateStarGiftCraftFail';
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiUpdateDeleteProfilePhoto = {
|
export type ApiUpdateDeleteProfilePhoto = {
|
||||||
'@type': 'updateDeleteProfilePhoto';
|
'@type': 'updateDeleteProfilePhoto';
|
||||||
peerId: string;
|
peerId: string;
|
||||||
@ -943,7 +947,8 @@ export type ApiUpdate = (
|
|||||||
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden |
|
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden |
|
||||||
ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage |
|
ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage |
|
||||||
ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateStarsBalance | ApiUpdateStarGiftAuctionState
|
ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateStarsBalance | ApiUpdateStarGiftAuctionState
|
||||||
| ApiUpdateStarGiftAuctionUserState | ApiUpdateBotCommands | ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies
|
| ApiUpdateStarGiftAuctionUserState | ApiUpdateStarGiftCraftFail | ApiUpdateBotCommands
|
||||||
|
| ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies
|
||||||
| ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages | ApiUpdateDeleteProfilePhoto | ApiUpdateNewProfilePhoto
|
| ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages | ApiUpdateDeleteProfilePhoto | ApiUpdateNewProfilePhoto
|
||||||
| ApiUpdateEntities | ApiUpdatePaidReactionPrivacy | ApiUpdateLangPackTooLong | ApiUpdateLangPack
|
| ApiUpdateEntities | ApiUpdatePaidReactionPrivacy | ApiUpdateLangPackTooLong | ApiUpdateLangPack
|
||||||
| ApiUpdateNotSupportedInFrozenAccountError
|
| ApiUpdateNotSupportedInFrozenAccountError
|
||||||
|
|||||||
1
src/assets/attribute-mask.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="29" fill="none"><defs><filter id="a" width="120%" height="120%" x="-10%" y="-10%"><feGaussianBlur in="SourceGraphic" stdDeviation=".3"/></filter></defs><path fill="#fff" d="M32 16c0 4.302-1.698 8.208-4.46 11.083-1.299 1.352-3.249 1.733-5.105 1.48-1.97-.27-4.55-.563-6.435-.563-1.886 0-4.466.293-6.434.562-1.857.254-3.807-.127-5.106-1.479A15.95 15.95 0 0 1 0 16C0 7.163 7.163 0 16 0s16 7.163 16 16" filter="url(#a)"/></svg>
|
||||||
|
After Width: | Height: | Size: 481 B |
1
src/assets/broken-gift.svg
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
1
src/assets/craft-progress.svg
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
1
src/assets/font-icons/add-filled.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 32 32"><path d="M16 0C7.163 0 0 7.163 0 16s7.163 16 16 16 16-7.163 16-16S24.837 0 16 0m7.5 17.5h-6v6a1.5 1.5 0 0 1-3 0v-6h-6a1.5 1.5 0 0 1 0-3h6v-6a1.5 1.5 0 0 1 3 0v6h6a1.5 1.5 0 0 1 0 3"/></svg>
|
||||||
|
After Width: | Height: | Size: 270 B |
1
src/assets/font-icons/boost-craft-chance.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M12.36 25.174c-1.506 0-2.732-1.226-2.732-2.732s1.226-2.731 2.732-2.731 2.731 1.226 2.731 2.731-1.225 2.732-2.731 2.732m0-3.463a.732.732 0 1 0 .002 1.464.732.732 0 0 0-.002-1.464M19.76 31.458c-1.507 0-2.732-1.226-2.732-2.731s1.225-2.732 2.731-2.732 2.732 1.226 2.732 2.732-1.226 2.731-2.732 2.731m0-3.463a.732.732 0 1 0 0 1.465.732.732 0 0 0 0-1.465M11.334 31.31a1 1 0 0 1-.707-1.707l9.452-9.452a1 1 0 1 1 1.414 1.414l-9.452 9.453a1 1 0 0 1-.707.293M22.315 17.215a1 1 0 0 1-.495-.132l-5.607-3.205-5.606 3.205a1 1 0 0 1-.992-1.736l6.598-3.772 6.599 3.772a1 1 0 0 1-.497 1.868"/><path d="M22.315 11.88a1 1 0 0 1-.495-.132l-5.607-3.204-5.606 3.204a1 1 0 1 1-.992-1.736l6.598-3.77 6.599 3.77a1 1 0 0 1-.497 1.868"/><path d="M24.819 27.818a1 1 0 0 1-.623-1.783c3.17-2.52 4.99-6.278 4.99-10.308 0-7.27-5.916-13.185-13.185-13.185-7.271 0-13.186 5.915-13.186 13.185 0 4.03 1.818 7.788 4.99 10.308a1 1 0 0 1-1.245 1.566C2.91 24.698.815 20.37.815 15.727.815 7.354 7.627.542 16 .542s15.185 6.812 15.185 15.185c0 4.643-2.094 8.971-5.745 11.874a1 1 0 0 1-.621.217"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/assets/font-icons/combine-craft.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M22.155.843H9.845C4.882.843.844 4.881.844 9.844v12.312c0 4.962 4.038 9 9 9h12.311c4.964 0 9.002-4.038 9.002-9V9.844c0-4.963-4.038-9.001-9.002-9.001m7.002 9.001V15h-2.404c-.437-1.552-1.864-2.692-3.554-2.692s-3.118 1.14-3.555 2.692H17v-4.507h-1a1.694 1.694 0 0 1-1.692-1.692c0-.933.76-1.691 1.692-1.691h1V2.843h5.155c3.861 0 7.002 3.14 7.002 7.001M9.845 2.843H15v2.404c-1.55.437-2.692 1.865-2.692 3.554S13.45 11.918 15 12.356V15h-4.507v1c0 .933-.759 1.692-1.692 1.692A1.693 1.693 0 0 1 7.109 16v-1H2.844V9.844c0-3.86 3.14-7.001 7-7.001M2.844 22.156V17h2.403c.436 1.551 1.864 2.692 3.554 2.692S11.918 18.55 12.355 17H15v4.507h1c.933 0 1.692.759 1.692 1.691 0 .934-.759 1.693-1.692 1.693h-1v4.265H9.845a7.01 7.01 0 0 1-7.001-7m19.311 7H17v-2.403c1.552-.436 2.692-1.865 2.692-3.555s-1.14-3.117-2.692-3.553V17h4.507v-1c0-.933.759-1.692 1.692-1.692s1.692.759 1.692 1.692v1h4.266v5.155c0 3.86-3.14 7.001-7.002 7.001" /></svg>
|
||||||
|
After Width: | Height: | Size: 986 B |
1
src/assets/font-icons/craft.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M11.945 19.473V15.37a.49.49 0 0 0-.484-.5H3.598a.494.494 0 0 0-.489.5c0 .113.04.223.106.309q1.553 2.016 3.41 3.02c1.285.695 2.504 1.167 4.45 1.417l.144.016c.36.047.68-.215.722-.582l.004-.04zM13.723 14.871h12.902a.49.49 0 0 1 .484.5v1.2a.497.497 0 0 1-.437.5q-1.745.17-2.969 1.109c-1.375 1.058-1.82 1.949-1.848 3.53-.015.962.426 1.892 1.325 2.782l.062.063a.66.66 0 0 1 .203.484v1.336c0 .367-.289.668-.648.668h-8.91c-.36 0-.649-.3-.649-.668v-1.21c0-.243.13-.466.336-.583q1.113-.634 1.114-1.953 0-1.354-1.176-1.942a.5.5 0 0 1-.274-.449v-4.867c0-.277.219-.5.485-.5M12.996 9.402l-.262-1.117a.664.664 0 0 1 .48-.805l.024-.007L25.2 5.07a1.224 1.224 0 0 1 1.426.934c.145.621-.227 1.246-.832 1.398l-.02.004-12.02 2.492a.643.643 0 0 1-.75-.464zM9.355 13.441l2.13-.55a1.17 1.17 0 0 0 .828-1.395l-.688-3.066a2.7 2.7 0 0 0-.375-.89l-.102-.157q-1.85-2.78-2.683-2.547-.846.239-1.211 3.707l-.008.094a2.7 2.7 0 0 0 .055.879l.687 3.054c.14.625.754 1.02 1.367.871m0 0"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -1576,6 +1576,7 @@
|
|||||||
"GiftInfoCollectible" = "Collectible #{number}";
|
"GiftInfoCollectible" = "Collectible #{number}";
|
||||||
"GiftInfoUniqueTitle" = "{name} {number}";
|
"GiftInfoUniqueTitle" = "{name} {number}";
|
||||||
"GiftSavedNumber" = "#{number}";
|
"GiftSavedNumber" = "#{number}";
|
||||||
|
"GiftInfoModelCrafted" = "{model} (crafted)";
|
||||||
"GiftAttributeModel" = "Model";
|
"GiftAttributeModel" = "Model";
|
||||||
"GiftAttributeBackdrop" = "Backdrop";
|
"GiftAttributeBackdrop" = "Backdrop";
|
||||||
"GiftAttributeSymbol" = "Symbol";
|
"GiftAttributeSymbol" = "Symbol";
|
||||||
@ -1593,6 +1594,32 @@
|
|||||||
"GiftInfoUpgradeBadge" = "upgrade";
|
"GiftInfoUpgradeBadge" = "upgrade";
|
||||||
"GiftInfoUpgradeForFree" = "Upgrade For Free";
|
"GiftInfoUpgradeForFree" = "Upgrade For Free";
|
||||||
"GiftInfoUpgrade" = "Upgrade";
|
"GiftInfoUpgrade" = "Upgrade";
|
||||||
|
"GiftInfoCraft" = "Craft";
|
||||||
|
"GiftCraftTitle" = "Craft Gift";
|
||||||
|
"GiftCraftButton" = "CRAFT {giftName}";
|
||||||
|
"GiftCraftSuccessChance" = "{percent} Success Chance";
|
||||||
|
"GiftCraftEmptyHint" = "Tap {button} to add your first gift";
|
||||||
|
"GiftCraftNewGift" = "Craft New Gift";
|
||||||
|
"GiftCraftSelectTitle" = "Select Gifts";
|
||||||
|
"GiftCraftSelectYourGifts" = "Your Gifts";
|
||||||
|
"GiftCraftSelectMarketGifts_one" = "{count} Suitable Gift on Sale";
|
||||||
|
"GiftCraftSelectMarketGifts_other" = "{count} Suitable Gifts on Sale";
|
||||||
|
"GiftCraftDescription" = "Add up to **4 gifts** to craft new {giftLine}";
|
||||||
|
"GiftCraftWarning" = "If crafting fails, all selected gifts will be consumed.";
|
||||||
|
"GiftCraftingTitle" = "Crafting";
|
||||||
|
"GiftCraftFailedTitle" = "Crafting Failed";
|
||||||
|
"GiftCraftFailedDescription_one" = "This crafting attempt was unsuccessful.\n**{count} gift** was lost.";
|
||||||
|
"GiftCraftFailedDescription_other" = "This crafting attempt was unsuccessful.\n**{count} gifts** were lost.";
|
||||||
|
"GiftCraftInfoTitle" = "Gift Crafting";
|
||||||
|
"GiftCraftInfoSubtitle" = "Turn your gifts into rare, epic, uncommon and legendary versions.";
|
||||||
|
"GiftCraftInfoCraftTitle" = "Get Rare Models";
|
||||||
|
"GiftCraftInfoCraftDescription" = "Select up to 4 gifts to craft a new exclusive model.";
|
||||||
|
"GiftCraftInfoChanceTitle" = "Maximize Chances";
|
||||||
|
"GiftCraftInfoChanceDescription" = "Combine more gifts to increase your odds of success.";
|
||||||
|
"GiftCraftInfoRiskTitle" = "Affect the Result";
|
||||||
|
"GiftCraftInfoRiskDescription" = "Use gifts with the same attribute to boost its chance.";
|
||||||
|
"GiftCraftHelp" = "Help";
|
||||||
|
"GiftCraftViewAll" = "View all craftable models >";
|
||||||
"GiftInfoWithdraw" = "Withdraw";
|
"GiftInfoWithdraw" = "Withdraw";
|
||||||
"GiftInfoWear" = "Wear";
|
"GiftInfoWear" = "Wear";
|
||||||
"GiftInfoTakeOff" = "Take Off";
|
"GiftInfoTakeOff" = "Take Off";
|
||||||
|
|||||||
BIN
src/assets/tgs/BrokenGift.tgs
Normal file
BIN
src/assets/tgs/CraftProgress.tgs
Normal file
@ -13,6 +13,9 @@ export { default as GiftInfoValueModal } from '../components/modals/gift/value/G
|
|||||||
export { default as GiftLockedModal } from '../components/modals/gift/locked/GiftLockedModal';
|
export { default as GiftLockedModal } from '../components/modals/gift/locked/GiftLockedModal';
|
||||||
export { default as GiftResalePriceComposerModal } from '../components/modals/gift/resale/GiftResalePriceComposerModal';
|
export { default as GiftResalePriceComposerModal } from '../components/modals/gift/resale/GiftResalePriceComposerModal';
|
||||||
export { default as GiftUpgradeModal } from '../components/modals/gift/upgrade/GiftUpgradeModal';
|
export { default as GiftUpgradeModal } from '../components/modals/gift/upgrade/GiftUpgradeModal';
|
||||||
|
export { default as GiftCraftModal } from '../components/modals/gift/craft/GiftCraftModal';
|
||||||
|
export { default as GiftCraftSelectModal } from '../components/modals/gift/craft/GiftCraftSelectModal';
|
||||||
|
export { default as GiftCraftInfoModal } from '../components/modals/gift/craft/GiftCraftInfoModal';
|
||||||
export { default as GiftPreviewModal } from '../components/modals/gift/preview/GiftPreviewModal';
|
export { default as GiftPreviewModal } from '../components/modals/gift/preview/GiftPreviewModal';
|
||||||
export { default as GiftAuctionModal } from '../components/modals/gift/auction/GiftAuctionModal';
|
export { default as GiftAuctionModal } from '../components/modals/gift/auction/GiftAuctionModal';
|
||||||
export { default as GiftAuctionBidModal } from '../components/modals/gift/auction/GiftAuctionBidModal';
|
export { default as GiftAuctionBidModal } from '../components/modals/gift/auction/GiftAuctionBidModal';
|
||||||
|
|||||||
@ -1,10 +1,21 @@
|
|||||||
import { memo, useRef } from '../../lib/teact/teact';
|
import { memo } from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { GlobalState } from '../../global/types';
|
||||||
|
|
||||||
|
import { selectTabState } from '../../global/selectors';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import buildStyle from '../../util/buildStyle';
|
import buildStyle from '../../util/buildStyle';
|
||||||
|
|
||||||
|
import useSelector from '../../hooks/data/useSelector';
|
||||||
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
|
|
||||||
import styles from './Sparkles.module.scss';
|
import styles from './Sparkles.module.scss';
|
||||||
|
|
||||||
|
function selectIsHeavyModalOpen(global: GlobalState) {
|
||||||
|
const tabState = selectTabState(global);
|
||||||
|
return Boolean(tabState.giftCraftModal || tabState.giftCraftSelectModal);
|
||||||
|
}
|
||||||
|
|
||||||
type ButtonParameters = {
|
type ButtonParameters = {
|
||||||
preset: 'button';
|
preset: 'button';
|
||||||
};
|
};
|
||||||
@ -89,7 +100,15 @@ const Sparkles = ({
|
|||||||
noAnimation,
|
noAnimation,
|
||||||
...presetSettings
|
...presetSettings
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
const ref = useRef<HTMLDivElement>();
|
const isHeavyModalOpen = useSelector(selectIsHeavyModalOpen);
|
||||||
|
|
||||||
|
const { ref, shouldRender } = useShowTransition<HTMLDivElement>({
|
||||||
|
isOpen: !isHeavyModalOpen,
|
||||||
|
withShouldRender: true,
|
||||||
|
noMountTransition: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!shouldRender) return undefined;
|
||||||
|
|
||||||
if (presetSettings.preset === 'button') {
|
if (presetSettings.preset === 'button') {
|
||||||
return (
|
return (
|
||||||
@ -124,7 +143,11 @@ const Sparkles = ({
|
|||||||
|
|
||||||
if (presetSettings.preset === 'progress') {
|
if (presetSettings.preset === 'progress') {
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className={buildClassName(styles.root, styles.progress, className)} style={style}>
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={buildClassName(styles.root, styles.progress, className)}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
{PROGRESS_POSITIONS.map((position) => {
|
{PROGRESS_POSITIONS.map((position) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -2,16 +2,12 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: -0.125rem;
|
top: -0.125rem;
|
||||||
right: -0.125rem;
|
right: -0.125rem;
|
||||||
|
|
||||||
width: 3.5rem;
|
|
||||||
height: 3.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%) translate(6px, -6px) rotate(45deg);
|
|
||||||
|
|
||||||
font-size: 0.625rem;
|
font-size: 0.625rem;
|
||||||
font-weight: var(--font-weight-semibold);
|
font-weight: var(--font-weight-semibold);
|
||||||
|
|||||||
@ -22,10 +22,15 @@ type ColorKey = keyof typeof COLORS;
|
|||||||
const COLOR_KEYS = new Set(Object.keys(COLORS) as ColorKey[]);
|
const COLOR_KEYS = new Set(Object.keys(COLORS) as ColorKey[]);
|
||||||
type GradientColor = readonly [string, string];
|
type GradientColor = readonly [string, string];
|
||||||
|
|
||||||
|
const DEFAULT_SIZE = 56;
|
||||||
|
const TEXT_OFFSET_RATIO = 6 / DEFAULT_SIZE;
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
color: ColorKey | GradientColor | (string & {});
|
color: ColorKey | GradientColor | (string & {});
|
||||||
text: string;
|
text: string;
|
||||||
|
size?: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
textClassName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -33,7 +38,7 @@ type StateProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const GiftRibbon = ({
|
const GiftRibbon = ({
|
||||||
text, color, className, theme,
|
text, color, size = DEFAULT_SIZE, className, textClassName, theme,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const randomId = useUniqueId();
|
const randomId = useUniqueId();
|
||||||
const validSvgRandomId = `svg-${randomId}`; // ID must start with a letter
|
const validSvgRandomId = `svg-${randomId}`; // ID must start with a letter
|
||||||
@ -52,9 +57,11 @@ const GiftRibbon = ({
|
|||||||
const startColor = gradientColor ? gradientColor[0] : color;
|
const startColor = gradientColor ? gradientColor[0] : color;
|
||||||
const endColor = gradientColor ? gradientColor[1] : color;
|
const endColor = gradientColor ? gradientColor[1] : color;
|
||||||
|
|
||||||
|
const textOffset = Math.round(size * TEXT_OFFSET_RATIO);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root, className)}>
|
<div className={buildClassName(styles.root, className)} style={`width: ${size}px; height: ${size}px`}>
|
||||||
<svg className={styles.ribbon} width="56" height="56" viewBox="0 0 56 56" fill="none">
|
<svg className={styles.ribbon} width={size} height={size} viewBox="0 0 56 56" fill="none" aria-hidden="true">
|
||||||
<path d="M52.4851 26.4853L29.5145 3.51472C27.2641 1.26428 24.2119 0 21.0293 0H2.82824C1.04643 0 0.154103 2.15429 1.41403 3.41422L52.5856 54.5858C53.8455 55.8457 55.9998 54.9534 55.9998 53.1716V34.9706C55.9998 31.788 54.7355 28.7357 52.4851 26.4853Z" fill={`url(#${validSvgRandomId})`} />
|
<path d="M52.4851 26.4853L29.5145 3.51472C27.2641 1.26428 24.2119 0 21.0293 0H2.82824C1.04643 0 0.154103 2.15429 1.41403 3.41422L52.5856 54.5858C53.8455 55.8457 55.9998 54.9534 55.9998 53.1716V34.9706C55.9998 31.788 54.7355 28.7357 52.4851 26.4853Z" fill={`url(#${validSvgRandomId})`} />
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id={validSvgRandomId} x1="27.9998" y1="1" x2="27.9998" y2="55" gradientUnits="userSpaceOnUse">
|
<linearGradient id={validSvgRandomId} x1="27.9998" y1="1" x2="27.9998" y2="55" gradientUnits="userSpaceOnUse">
|
||||||
@ -63,7 +70,12 @@ const GiftRibbon = ({
|
|||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
<div className={styles.text}>{text}</div>
|
<div
|
||||||
|
className={buildClassName(styles.text, textClassName)}
|
||||||
|
style={`transform: translate(-50%, -50%) translate(${textOffset}px, ${-textOffset}px) rotate(45deg)`}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import BrokenGiftPreview from '../../../assets/broken-gift.svg';
|
||||||
import QrPlane from '../../../assets/tgs/auth/QrPlane.tgs';
|
import QrPlane from '../../../assets/tgs/auth/QrPlane.tgs';
|
||||||
import BannedDuck from '../../../assets/tgs/BannedDuck.tgs';
|
import BannedDuck from '../../../assets/tgs/BannedDuck.tgs';
|
||||||
|
import BrokenGift from '../../../assets/tgs/BrokenGift.tgs';
|
||||||
import CameraFlip from '../../../assets/tgs/calls/CameraFlip.tgs';
|
import CameraFlip from '../../../assets/tgs/calls/CameraFlip.tgs';
|
||||||
import HandFilled from '../../../assets/tgs/calls/HandFilled.tgs';
|
import HandFilled from '../../../assets/tgs/calls/HandFilled.tgs';
|
||||||
import HandOutline from '../../../assets/tgs/calls/HandOutline.tgs';
|
import HandOutline from '../../../assets/tgs/calls/HandOutline.tgs';
|
||||||
@ -8,6 +10,7 @@ import VoiceAllowTalk from '../../../assets/tgs/calls/VoiceAllowTalk.tgs';
|
|||||||
import VoiceMini from '../../../assets/tgs/calls/VoiceMini.tgs';
|
import VoiceMini from '../../../assets/tgs/calls/VoiceMini.tgs';
|
||||||
import VoiceMuted from '../../../assets/tgs/calls/VoiceMuted.tgs';
|
import VoiceMuted from '../../../assets/tgs/calls/VoiceMuted.tgs';
|
||||||
import VoiceOutlined from '../../../assets/tgs/calls/VoiceOutlined.tgs';
|
import VoiceOutlined from '../../../assets/tgs/calls/VoiceOutlined.tgs';
|
||||||
|
import CraftProgress from '../../../assets/tgs/CraftProgress.tgs';
|
||||||
import Diamond from '../../../assets/tgs/Diamond.tgs';
|
import Diamond from '../../../assets/tgs/Diamond.tgs';
|
||||||
import DuckNothingFound from '../../../assets/tgs/DuckNothingFound.tgs';
|
import DuckNothingFound from '../../../assets/tgs/DuckNothingFound.tgs';
|
||||||
import Flame from '../../../assets/tgs/general/Flame.tgs';
|
import Flame from '../../../assets/tgs/general/Flame.tgs';
|
||||||
@ -42,6 +45,7 @@ import SearchPreview from '../../../assets/tgs-previews/Search.svg';
|
|||||||
import PasskeysPreview from '../../../assets/tgs-previews/settings/Passkeys.svg';
|
import PasskeysPreview from '../../../assets/tgs-previews/settings/Passkeys.svg';
|
||||||
|
|
||||||
export const LOCAL_TGS_PREVIEW_URLS = {
|
export const LOCAL_TGS_PREVIEW_URLS = {
|
||||||
|
BrokenGift: BrokenGiftPreview,
|
||||||
DuckNothingFound: DuckNothingFoundPreview,
|
DuckNothingFound: DuckNothingFoundPreview,
|
||||||
Search: SearchPreview,
|
Search: SearchPreview,
|
||||||
Passkeys: PasskeysPreview,
|
Passkeys: PasskeysPreview,
|
||||||
@ -82,6 +86,8 @@ export const LOCAL_TGS_URLS = {
|
|||||||
Report,
|
Report,
|
||||||
SearchingDuck,
|
SearchingDuck,
|
||||||
BannedDuck,
|
BannedDuck,
|
||||||
|
BrokenGift,
|
||||||
|
CraftProgress,
|
||||||
Diamond,
|
Diamond,
|
||||||
Search,
|
Search,
|
||||||
DuckNothingFound,
|
DuckNothingFound,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { memo, useEffect, useLayoutEffect, useMemo, useRef, useSignal, useState } from '../../../lib/teact/teact';
|
import { memo, useEffect, useLayoutEffect, useMemo, useRef, useSignal } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiSticker } from '../../../api/types';
|
import type { ApiSticker } from '../../../api/types';
|
||||||
|
|
||||||
@ -10,6 +10,7 @@ import { adjustHsv, getColorLuma, hex2rgb } from '../../../util/colors.ts';
|
|||||||
import { preloadImage } from '../../../util/files';
|
import { preloadImage } from '../../../util/files';
|
||||||
import { REM } from '../helpers/mediaDimensions';
|
import { REM } from '../helpers/mediaDimensions';
|
||||||
|
|
||||||
|
import useAsync from '../../../hooks/useAsync';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
import useResizeObserver from '../../../hooks/useResizeObserver';
|
import useResizeObserver from '../../../hooks/useResizeObserver';
|
||||||
@ -20,7 +21,8 @@ import styles from './RadialPatternBackground.module.scss';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
backgroundColors?: string[];
|
backgroundColors?: string[];
|
||||||
patternIcon?: ApiSticker;
|
patternIcon?: ApiSticker;
|
||||||
patternColor?: number;
|
patternUrl?: string;
|
||||||
|
patternColor?: string;
|
||||||
patternSize?: number;
|
patternSize?: number;
|
||||||
maxRadius?: number;
|
maxRadius?: number;
|
||||||
centerEmptiness?: number;
|
centerEmptiness?: number;
|
||||||
@ -48,6 +50,8 @@ const DEFAULT_OVAL_FACTOR = 1.4;
|
|||||||
const RadialPatternBackground = ({
|
const RadialPatternBackground = ({
|
||||||
backgroundColors,
|
backgroundColors,
|
||||||
patternIcon,
|
patternIcon,
|
||||||
|
patternUrl,
|
||||||
|
patternColor,
|
||||||
patternSize = DEFAULT_PATTERN_SIZE,
|
patternSize = DEFAULT_PATTERN_SIZE,
|
||||||
centerEmptiness = DEFAULT_CENTER_EMPTINESS,
|
centerEmptiness = DEFAULT_CENTER_EMPTINESS,
|
||||||
ringsCount = DEFAULT_RINGS_COUNT,
|
ringsCount = DEFAULT_RINGS_COUNT,
|
||||||
@ -67,15 +71,15 @@ const RadialPatternBackground = ({
|
|||||||
|
|
||||||
const dpr = useDevicePixelRatio();
|
const dpr = useDevicePixelRatio();
|
||||||
|
|
||||||
const [emojiImage, setEmojiImage] = useState<HTMLImageElement | undefined>();
|
|
||||||
|
|
||||||
const previewMediaHash = patternIcon && getStickerMediaHash(patternIcon, 'preview');
|
const previewMediaHash = patternIcon && getStickerMediaHash(patternIcon, 'preview');
|
||||||
const previewUrl = useMedia(previewMediaHash);
|
const previewUrl = useMedia(previewMediaHash);
|
||||||
|
|
||||||
useEffect(() => {
|
const imageUrl = previewUrl || patternUrl;
|
||||||
if (!previewUrl) return;
|
|
||||||
preloadImage(previewUrl).then(setEmojiImage);
|
const { result: emojiImage } = useAsync(
|
||||||
}, [previewUrl]);
|
() => (imageUrl ? preloadImage(imageUrl) : Promise.resolve(undefined)),
|
||||||
|
[imageUrl],
|
||||||
|
);
|
||||||
|
|
||||||
const patternPositions = useMemo(() => {
|
const patternPositions = useMemo(() => {
|
||||||
const coordinates: { x: number; y: number; sizeFactor: number }[] = [];
|
const coordinates: { x: number; y: number; sizeFactor: number }[] = [];
|
||||||
@ -146,9 +150,13 @@ const RadialPatternBackground = ({
|
|||||||
ctx.drawImage(emojiImage, renderX - size / 2, renderY - size / 2, size, size);
|
ctx.drawImage(emojiImage, renderX - size / 2, renderY - size / 2, size, size);
|
||||||
});
|
});
|
||||||
|
|
||||||
const patternColor = backgroundColors?.[1] ?? backgroundColors?.[0] ?? '#000000';
|
if (patternColor) {
|
||||||
const isDark = getColorLuma(hex2rgb(patternColor)) < DARK_LUMA_THRESHOLD;
|
ctx.fillStyle = patternColor;
|
||||||
ctx.fillStyle = adjustHsv(patternColor, 0.5, isDark ? 0.28 : -0.28);
|
} else {
|
||||||
|
const baseColor = backgroundColors?.[1] ?? backgroundColors?.[0] ?? '#000000';
|
||||||
|
const isDark = getColorLuma(hex2rgb(baseColor)) < DARK_LUMA_THRESHOLD;
|
||||||
|
ctx.fillStyle = adjustHsv(baseColor, 0.5, isDark ? 0.28 : -0.28);
|
||||||
|
}
|
||||||
ctx.globalCompositeOperation = 'source-in';
|
ctx.globalCompositeOperation = 'source-in';
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
@ -171,7 +179,7 @@ const RadialPatternBackground = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
draw();
|
draw();
|
||||||
}, [emojiImage, patternPositions, yPosition, ovalFactor]);
|
}, [emojiImage, patternPositions, yPosition, ovalFactor, patternColor]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const { width, height } = getContainerSize();
|
const { width, height } = getContainerSize();
|
||||||
|
|||||||
@ -702,6 +702,17 @@ const ActionMessageText = ({
|
|||||||
if (isSavedMessages) {
|
if (isSavedMessages) {
|
||||||
if (isUpgrade) return lang('ActionStarGiftUpgradedSelf');
|
if (isUpgrade) return lang('ActionStarGiftUpgradedSelf');
|
||||||
if (isTransferred) return lang('ActionStarGiftTransferredSelf');
|
if (isTransferred) return lang('ActionStarGiftTransferredSelf');
|
||||||
|
if (resaleAmount) {
|
||||||
|
const amountText = formatCurrencyAmountAsText(lang, resaleAmount);
|
||||||
|
return lang(
|
||||||
|
'ApiMessageMessageActionResaleStarGiftUniqueOutgoing',
|
||||||
|
{
|
||||||
|
gift: lang('GiftUnique', { title: gift.title, number: gift.number }),
|
||||||
|
stars: asPreview ? amountText : renderStrong(amountText),
|
||||||
|
},
|
||||||
|
{ withNodes: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
if (gift.isCrafted) return lang('ActionStarGiftCraftedSelf');
|
if (gift.isCrafted) return lang('ActionStarGiftCraftedSelf');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,9 @@ import GiftAuctionBidModal from './gift/auction/GiftAuctionBidModal.async';
|
|||||||
import GiftAuctionChangeRecipientModal from './gift/auction/GiftAuctionChangeRecipientModal.async';
|
import GiftAuctionChangeRecipientModal from './gift/auction/GiftAuctionChangeRecipientModal.async';
|
||||||
import GiftAuctionInfoModal from './gift/auction/GiftAuctionInfoModal.async';
|
import GiftAuctionInfoModal from './gift/auction/GiftAuctionInfoModal.async';
|
||||||
import GiftAuctionModal from './gift/auction/GiftAuctionModal.async';
|
import GiftAuctionModal from './gift/auction/GiftAuctionModal.async';
|
||||||
|
import GiftCraftInfoModal from './gift/craft/GiftCraftInfoModal.async';
|
||||||
|
import GiftCraftModal from './gift/craft/GiftCraftModal.async';
|
||||||
|
import GiftCraftSelectModal from './gift/craft/GiftCraftSelectModal.async';
|
||||||
import PremiumGiftModal from './gift/GiftModal.async';
|
import PremiumGiftModal from './gift/GiftModal.async';
|
||||||
import GiftInfoModal from './gift/info/GiftInfoModal.async';
|
import GiftInfoModal from './gift/info/GiftInfoModal.async';
|
||||||
import GiftLockedModal from './gift/locked/GiftLockedModal.async';
|
import GiftLockedModal from './gift/locked/GiftLockedModal.async';
|
||||||
@ -108,6 +111,9 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'aboutAdsModal' |
|
'aboutAdsModal' |
|
||||||
'giftPreviewModal' |
|
'giftPreviewModal' |
|
||||||
'giftUpgradeModal' |
|
'giftUpgradeModal' |
|
||||||
|
'giftCraftModal' |
|
||||||
|
'giftCraftSelectModal' |
|
||||||
|
'giftCraftInfoModal' |
|
||||||
'giftAuctionModal' |
|
'giftAuctionModal' |
|
||||||
'giftAuctionBidModal' |
|
'giftAuctionBidModal' |
|
||||||
'giftAuctionInfoModal' |
|
'giftAuctionInfoModal' |
|
||||||
@ -188,6 +194,9 @@ const MODALS: ModalRegistry = {
|
|||||||
aboutAdsModal: AboutAdsModal,
|
aboutAdsModal: AboutAdsModal,
|
||||||
giftPreviewModal: GiftPreviewModal,
|
giftPreviewModal: GiftPreviewModal,
|
||||||
giftUpgradeModal: GiftUpgradeModal,
|
giftUpgradeModal: GiftUpgradeModal,
|
||||||
|
giftCraftModal: GiftCraftModal,
|
||||||
|
giftCraftSelectModal: GiftCraftSelectModal,
|
||||||
|
giftCraftInfoModal: GiftCraftInfoModal,
|
||||||
giftAuctionModal: GiftAuctionModal,
|
giftAuctionModal: GiftAuctionModal,
|
||||||
giftAuctionBidModal: GiftAuctionBidModal,
|
giftAuctionBidModal: GiftAuctionBidModal,
|
||||||
giftAuctionInfoModal: GiftAuctionInfoModal,
|
giftAuctionInfoModal: GiftAuctionInfoModal,
|
||||||
|
|||||||
@ -32,6 +32,7 @@ type OwnProps = {
|
|||||||
hasBackdrop?: boolean;
|
hasBackdrop?: boolean;
|
||||||
closeButtonColor?: 'translucent' | 'translucent-white';
|
closeButtonColor?: 'translucent' | 'translucent-white';
|
||||||
moreMenuItems?: TeactNode;
|
moreMenuItems?: TeactNode;
|
||||||
|
headerRightToolBar?: TeactNode;
|
||||||
onClose: NoneToVoidFunction;
|
onClose: NoneToVoidFunction;
|
||||||
onButtonClick?: NoneToVoidFunction;
|
onButtonClick?: NoneToVoidFunction;
|
||||||
withBalanceBar?: boolean;
|
withBalanceBar?: boolean;
|
||||||
@ -54,6 +55,7 @@ const TableInfoModal = ({
|
|||||||
hasBackdrop,
|
hasBackdrop,
|
||||||
closeButtonColor,
|
closeButtonColor,
|
||||||
moreMenuItems,
|
moreMenuItems,
|
||||||
|
headerRightToolBar,
|
||||||
onClose,
|
onClose,
|
||||||
onButtonClick,
|
onButtonClick,
|
||||||
withBalanceBar,
|
withBalanceBar,
|
||||||
@ -79,6 +81,7 @@ const TableInfoModal = ({
|
|||||||
className={className}
|
className={className}
|
||||||
contentClassName={buildClassName(styles.content, contentClassName)}
|
contentClassName={buildClassName(styles.content, contentClassName)}
|
||||||
moreMenuItems={moreMenuItems}
|
moreMenuItems={moreMenuItems}
|
||||||
|
headerRightToolBar={headerRightToolBar}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
withBalanceBar={withBalanceBar}
|
withBalanceBar={withBalanceBar}
|
||||||
currencyInBalanceBar={currencyInBalanceBar}
|
currencyInBalanceBar={currencyInBalanceBar}
|
||||||
|
|||||||
41
src/components/modals/gift/GiftEmptyState.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import type { TeactNode } from '../../../lib/teact/teact';
|
||||||
|
import { memo } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
||||||
|
|
||||||
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
|
import Link from '../../ui/Link';
|
||||||
|
|
||||||
|
import styles from './GiftEmptyState.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
description: TeactNode;
|
||||||
|
linkText?: TeactNode;
|
||||||
|
onLinkClick?: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
const GiftEmptyState = ({ description, linkText, onLinkClick }: OwnProps) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.root}>
|
||||||
|
<AnimatedIconWithPreview
|
||||||
|
size={160}
|
||||||
|
tgsUrl={LOCAL_TGS_URLS.SearchingDuck}
|
||||||
|
nonInteractive
|
||||||
|
noLoop
|
||||||
|
/>
|
||||||
|
<div className={styles.description}>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
{Boolean(linkText && onLinkClick) && (
|
||||||
|
<Link
|
||||||
|
className={styles.link}
|
||||||
|
onClick={onLinkClick}
|
||||||
|
>
|
||||||
|
{linkText}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(GiftEmptyState);
|
||||||
@ -14,18 +14,16 @@ import { selectTabState,
|
|||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { RESALE_GIFTS_LIMIT } from '../../../limits';
|
import { RESALE_GIFTS_LIMIT } from '../../../limits';
|
||||||
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
|
||||||
|
|
||||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Link from '../../ui/Link';
|
|
||||||
import Transition from '../../ui/Transition';
|
import Transition from '../../ui/Transition';
|
||||||
import GiftItemStar from './GiftItemStar';
|
import GiftItemStar from './GiftItemStar';
|
||||||
|
import ResaleGiftsNotFound from './ResaleGiftsNotFound';
|
||||||
|
|
||||||
import styles from './GiftModal.module.scss';
|
import styles from './GiftModal.module.scss';
|
||||||
|
|
||||||
@ -96,37 +94,19 @@ const GiftModalResaleScreen: FC<OwnProps & StateProps> = ({
|
|||||||
} });
|
} });
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderNothingFoundGiftsWithFilter() {
|
|
||||||
return (
|
|
||||||
<div className={styles.notFoundGiftsRoot}>
|
|
||||||
<AnimatedIconWithPreview
|
|
||||||
size={160}
|
|
||||||
tgsUrl={LOCAL_TGS_URLS.SearchingDuck}
|
|
||||||
nonInteractive
|
|
||||||
noLoop
|
|
||||||
/>
|
|
||||||
<div className={styles.notFoundGiftsDescription}>
|
|
||||||
{lang('ResellGiftsNoFound')}
|
|
||||||
</div>
|
|
||||||
{hasFilter && (
|
|
||||||
<Link
|
|
||||||
className={styles.notFoundGiftsLink}
|
|
||||||
onClick={handleResetGiftsFilter}
|
|
||||||
>
|
|
||||||
{lang('ResellGiftsClearFilters')}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={scrollerRef} className={buildClassName(styles.resaleScreenRoot, 'custom-scroll')}>
|
<div ref={scrollerRef} className={buildClassName(styles.resaleScreenRoot, 'custom-scroll')}>
|
||||||
<Transition
|
<Transition
|
||||||
name="zoomFade"
|
name="zoomFade"
|
||||||
activeKey={updateIteration}
|
activeKey={updateIteration}
|
||||||
>
|
>
|
||||||
{isGiftsEmpty && areGiftsAllLoaded && renderNothingFoundGiftsWithFilter()}
|
{isGiftsEmpty && areGiftsAllLoaded && (
|
||||||
|
<ResaleGiftsNotFound
|
||||||
|
description={lang('ResellGiftsNoFound')}
|
||||||
|
linkText={hasFilter ? lang('ResellGiftsClearFilters') : undefined}
|
||||||
|
onLinkClick={hasFilter ? handleResetGiftsFilter : undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
className={buildClassName(styles.resaleStarGiftsContainer)}
|
className={buildClassName(styles.resaleStarGiftsContainer)}
|
||||||
items={viewportIds}
|
items={viewportIds}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { type MouseEvent as ReactMouseEvent } from 'react';
|
import { type MouseEvent as ReactMouseEvent } from 'react';
|
||||||
import type { ElementRef, FC } from '../../../lib/teact/teact';
|
import type { ElementRef } from '../../../lib/teact/teact';
|
||||||
import type React from '../../../lib/teact/teact';
|
import type React from '../../../lib/teact/teact';
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
@ -39,8 +39,12 @@ import ResaleGiftMenuAttributeSticker from './ResaleGiftMenuAttributeSticker';
|
|||||||
|
|
||||||
import styles from './GiftResaleFilters.module.scss';
|
import styles from './GiftResaleFilters.module.scss';
|
||||||
|
|
||||||
|
type FilterType = 'resale' | 'craft';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
dialogRef: ElementRef<HTMLDivElement>;
|
dialogRef: ElementRef<HTMLDivElement>;
|
||||||
|
className?: string;
|
||||||
|
filterType?: FilterType;
|
||||||
};
|
};
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
filter: ResaleGiftsFilterOptions;
|
filter: ResaleGiftsFilterOptions;
|
||||||
@ -48,15 +52,20 @@ type StateProps = {
|
|||||||
counters?: ApiStarGiftAttributeCounter[];
|
counters?: ApiStarGiftAttributeCounter[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
const DEFAULT_CRAFT_FILTER: ResaleGiftsFilterOptions = { sortType: 'byPrice' };
|
||||||
|
|
||||||
|
const GiftResaleFilters = ({
|
||||||
attributes,
|
attributes,
|
||||||
counters,
|
counters,
|
||||||
filter,
|
filter,
|
||||||
dialogRef,
|
dialogRef,
|
||||||
}) => {
|
className,
|
||||||
|
filterType = 'resale',
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const {
|
const {
|
||||||
updateResaleGiftsFilter,
|
updateResaleGiftsFilter,
|
||||||
|
updateCraftGiftsFilter,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const [searchModelQuery, setSearchModelQuery] = useState('');
|
const [searchModelQuery, setSearchModelQuery] = useState('');
|
||||||
@ -193,100 +202,114 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
} = useContextMenuHandlers(dialogRef);
|
} = useContextMenuHandlers(dialogRef);
|
||||||
const getPatternMenuElement = useLastCallback(() => patternMenuRef.current!);
|
const getPatternMenuElement = useLastCallback(() => patternMenuRef.current!);
|
||||||
|
|
||||||
const SortMenuButton: FC<{ onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void; isOpen?: boolean }>
|
const SortMenuButton = useMemo(() => {
|
||||||
= useMemo(() => {
|
const sortType = filter.sortType;
|
||||||
const sortType = filter.sortType;
|
const iconName = sortType === 'byDate' ? 'sort-by-date'
|
||||||
const iconName = sortType === 'byDate' ? 'sort-by-date'
|
: sortType === 'byNumber' ? 'sort-by-number'
|
||||||
: sortType === 'byNumber' ? 'sort-by-number'
|
: 'sort-by-price';
|
||||||
: 'sort-by-price';
|
return ({ onTrigger, isOpen: isMenuOpen }: {
|
||||||
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
<div
|
isOpen?: boolean;
|
||||||
className={styles.item}
|
}) => (
|
||||||
onClick={onTrigger}
|
<div
|
||||||
>
|
className={styles.item}
|
||||||
<Icon
|
onClick={onTrigger}
|
||||||
name={iconName}
|
>
|
||||||
className={styles.itemIcon}
|
<Icon
|
||||||
/>
|
name={iconName}
|
||||||
{sortType === 'byDate' && lang('ValueGiftSortByDate')}
|
className={styles.itemIcon}
|
||||||
{sortType === 'byNumber' && lang('ValueGiftSortByNumber')}
|
/>
|
||||||
{sortType === 'byPrice' && lang('ValueGiftSortByPrice')}
|
{sortType === 'byDate' && lang('ValueGiftSortByDate')}
|
||||||
</div>
|
{sortType === 'byNumber' && lang('ValueGiftSortByNumber')}
|
||||||
);
|
{sortType === 'byPrice' && lang('ValueGiftSortByPrice')}
|
||||||
}, [lang, filter]);
|
</div>
|
||||||
|
);
|
||||||
|
}, [lang, filter]);
|
||||||
|
|
||||||
const ModelMenuButton:
|
const ModelMenuButton = useMemo(() => {
|
||||||
FC<{ onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void; isOpen?: boolean }>
|
const attributesCount = filter?.modelAttributes?.length || 0;
|
||||||
= useMemo(() => {
|
return ({ onTrigger, isOpen: isMenuOpen }: {
|
||||||
const attributesCount = filter?.modelAttributes?.length || 0;
|
onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
isOpen?: boolean;
|
||||||
<div
|
}) => (
|
||||||
className={styles.item}
|
<div
|
||||||
onClick={onTrigger}
|
className={styles.item}
|
||||||
>
|
onClick={onTrigger}
|
||||||
{attributesCount === 0 && lang('GiftAttributeModel')}
|
>
|
||||||
{attributesCount > 0
|
{attributesCount === 0 && lang('GiftAttributeModel')}
|
||||||
&& lang('GiftAttributeModelPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
{attributesCount > 0
|
||||||
{renderDropdownArrows(isMenuOpen)}
|
&& lang('GiftAttributeModelPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
||||||
</div>
|
{renderDropdownArrows(isMenuOpen)}
|
||||||
);
|
</div>
|
||||||
}, [lang, filter]);
|
);
|
||||||
const BackdropMenuButton:
|
}, [lang, filter]);
|
||||||
FC<{ onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void; isOpen?: boolean }>
|
const BackdropMenuButton = useMemo(() => {
|
||||||
= useMemo(() => {
|
const attributesCount = filter?.backdropAttributes?.length || 0;
|
||||||
const attributesCount = filter?.backdropAttributes?.length || 0;
|
return ({ onTrigger, isOpen: isMenuOpen }: {
|
||||||
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
<div
|
isOpen?: boolean;
|
||||||
className={styles.item}
|
}) => (
|
||||||
onClick={onTrigger}
|
<div
|
||||||
>
|
className={styles.item}
|
||||||
{attributesCount === 0 && lang('GiftAttributeBackdrop')}
|
onClick={onTrigger}
|
||||||
{attributesCount > 0
|
>
|
||||||
&& lang('GiftAttributeBackdropPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
{attributesCount === 0 && lang('GiftAttributeBackdrop')}
|
||||||
{renderDropdownArrows(isMenuOpen)}
|
{attributesCount > 0
|
||||||
</div>
|
&& lang('GiftAttributeBackdropPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
||||||
);
|
{renderDropdownArrows(isMenuOpen)}
|
||||||
}, [lang, filter]);
|
</div>
|
||||||
const PatternMenuButton: FC<{ onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void; isOpen?: boolean }>
|
);
|
||||||
= useMemo(() => {
|
}, [lang, filter]);
|
||||||
const attributesCount = filter?.patternAttributes?.length || 0;
|
const PatternMenuButton = useMemo(() => {
|
||||||
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
const attributesCount = filter?.patternAttributes?.length || 0;
|
||||||
<div
|
return ({ onTrigger, isOpen: isMenuOpen }: {
|
||||||
className={styles.item}
|
onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
onClick={onTrigger}
|
isOpen?: boolean;
|
||||||
>
|
}) => (
|
||||||
{attributesCount === 0 && lang('GiftAttributeSymbol')}
|
<div
|
||||||
{attributesCount > 0
|
className={styles.item}
|
||||||
&& lang('GiftAttributeSymbolPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
onClick={onTrigger}
|
||||||
{renderDropdownArrows(isMenuOpen)}
|
>
|
||||||
</div>
|
{attributesCount === 0 && lang('GiftAttributeSymbol')}
|
||||||
);
|
{attributesCount > 0
|
||||||
}, [lang, filter]);
|
&& lang('GiftAttributeSymbolPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
||||||
|
{renderDropdownArrows(isMenuOpen)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}, [lang, filter]);
|
||||||
|
|
||||||
|
const handleFilterUpdate = useLastCallback((newFilter: ResaleGiftsFilterOptions) => {
|
||||||
|
if (filterType === 'craft') {
|
||||||
|
updateCraftGiftsFilter({ filter: newFilter });
|
||||||
|
} else {
|
||||||
|
updateResaleGiftsFilter({ filter: newFilter });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const handleSortMenuItemClick = useLastCallback((type: ResaleGiftsSortType) => {
|
const handleSortMenuItemClick = useLastCallback((type: ResaleGiftsSortType) => {
|
||||||
updateResaleGiftsFilter({ filter: {
|
handleFilterUpdate({
|
||||||
...filter,
|
...filter,
|
||||||
sortType: type,
|
sortType: type,
|
||||||
} });
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSelectedAllModelsClick = useLastCallback(() => {
|
const handleSelectedAllModelsClick = useLastCallback(() => {
|
||||||
updateResaleGiftsFilter({ filter: {
|
handleFilterUpdate({
|
||||||
...filter,
|
...filter,
|
||||||
modelAttributes: [],
|
modelAttributes: [],
|
||||||
} });
|
});
|
||||||
});
|
});
|
||||||
const handleSelectedAllPatternsClick = useLastCallback(() => {
|
const handleSelectedAllPatternsClick = useLastCallback(() => {
|
||||||
updateResaleGiftsFilter({ filter: {
|
handleFilterUpdate({
|
||||||
...filter,
|
...filter,
|
||||||
patternAttributes: [],
|
patternAttributes: [],
|
||||||
} });
|
});
|
||||||
});
|
});
|
||||||
const handleSelectedAllBackdropsClick = useLastCallback(() => {
|
const handleSelectedAllBackdropsClick = useLastCallback(() => {
|
||||||
updateResaleGiftsFilter({ filter: {
|
handleFilterUpdate({
|
||||||
...filter,
|
...filter,
|
||||||
backdropAttributes: [],
|
backdropAttributes: [],
|
||||||
} });
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleModelMenuItemClick = useLastCallback((attribute: ApiStarGiftAttributeModel) => {
|
const handleModelMenuItemClick = useLastCallback((attribute: ApiStarGiftAttributeModel) => {
|
||||||
@ -303,10 +326,10 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
const updatedAttributes = isActive
|
const updatedAttributes = isActive
|
||||||
? modelAttributes.filter((item) => item.documentId !== modelAttribute.documentId)
|
? modelAttributes.filter((item) => item.documentId !== modelAttribute.documentId)
|
||||||
: [...modelAttributes, modelAttribute];
|
: [...modelAttributes, modelAttribute];
|
||||||
updateResaleGiftsFilter({ filter: {
|
handleFilterUpdate({
|
||||||
...filter,
|
...filter,
|
||||||
modelAttributes: updatedAttributes,
|
modelAttributes: updatedAttributes,
|
||||||
} });
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handlePatternMenuItemClick = useLastCallback((attribute: ApiStarGiftAttributePattern) => {
|
const handlePatternMenuItemClick = useLastCallback((attribute: ApiStarGiftAttributePattern) => {
|
||||||
@ -323,10 +346,10 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
const updatedAttributes = isActive
|
const updatedAttributes = isActive
|
||||||
? patternAttributes.filter((item) => item.documentId !== patternAttribute.documentId)
|
? patternAttributes.filter((item) => item.documentId !== patternAttribute.documentId)
|
||||||
: [...patternAttributes, patternAttribute];
|
: [...patternAttributes, patternAttribute];
|
||||||
updateResaleGiftsFilter({ filter: {
|
handleFilterUpdate({
|
||||||
...filter,
|
...filter,
|
||||||
patternAttributes: updatedAttributes,
|
patternAttributes: updatedAttributes,
|
||||||
} });
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleBackdropMenuItemClick = useLastCallback((attribute: ApiStarGiftAttributeBackdrop) => {
|
const handleBackdropMenuItemClick = useLastCallback((attribute: ApiStarGiftAttributeBackdrop) => {
|
||||||
@ -343,10 +366,10 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
const updatedAttributes = isActive
|
const updatedAttributes = isActive
|
||||||
? backdropAttributes.filter((item) => item.backdropId !== backdropAttribute.backdropId)
|
? backdropAttributes.filter((item) => item.backdropId !== backdropAttribute.backdropId)
|
||||||
: [...backdropAttributes, backdropAttribute];
|
: [...backdropAttributes, backdropAttribute];
|
||||||
updateResaleGiftsFilter({ filter: {
|
handleFilterUpdate({
|
||||||
...filter,
|
...filter,
|
||||||
backdropAttributes: updatedAttributes,
|
backdropAttributes: updatedAttributes,
|
||||||
} });
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderDropdownArrows(isOpen?: boolean) {
|
function renderDropdownArrows(isOpen?: boolean) {
|
||||||
@ -648,7 +671,7 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root}>
|
<div className={buildClassName(styles.root, className)}>
|
||||||
{Boolean(sortContextMenuAnchor) && renderSortMenu()}
|
{Boolean(sortContextMenuAnchor) && renderSortMenu()}
|
||||||
{Boolean(modelContextMenuAnchor) && renderModelMenu()}
|
{Boolean(modelContextMenuAnchor) && renderModelMenu()}
|
||||||
{Boolean(backdropContextMenuAnchor) && renderBackdropMenu()}
|
{Boolean(backdropContextMenuAnchor) && renderBackdropMenu()}
|
||||||
@ -675,16 +698,22 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal((global): Complete<StateProps> => {
|
export default memo(withGlobal<OwnProps>((global, { filterType }): Complete<StateProps> => {
|
||||||
const { resaleGifts } = selectTabState(global);
|
const tabState = selectTabState(global);
|
||||||
|
|
||||||
const attributes = resaleGifts.attributes;
|
if (filterType === 'craft') {
|
||||||
const counters = resaleGifts.counters;
|
const craftModal = tabState.giftCraftModal;
|
||||||
const filter = resaleGifts.filter;
|
return {
|
||||||
|
filter: craftModal?.marketFilter || DEFAULT_CRAFT_FILTER,
|
||||||
|
attributes: craftModal?.marketAttributes,
|
||||||
|
counters: craftModal?.marketCounters,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resaleGifts } = tabState;
|
||||||
return {
|
return {
|
||||||
attributes,
|
filter: resaleGifts.filter,
|
||||||
counters,
|
attributes: resaleGifts.attributes,
|
||||||
filter,
|
counters: resaleGifts.counters,
|
||||||
};
|
};
|
||||||
})(GiftResaleFilters));
|
})(GiftResaleFilters));
|
||||||
|
|||||||
30
src/components/modals/gift/ResaleGiftsNotFound.module.scss
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
padding-top: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin-block: 1rem;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: var(--color-links);
|
||||||
|
transition: opacity 0.15s ease-in;
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-links);
|
||||||
|
text-decoration: none;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/components/modals/gift/ResaleGiftsNotFound.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { TeactNode } from '../../../lib/teact/teact';
|
||||||
|
import { memo } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
||||||
|
|
||||||
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
|
import Link from '../../ui/Link';
|
||||||
|
|
||||||
|
import styles from './ResaleGiftsNotFound.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
className?: string;
|
||||||
|
description: TeactNode;
|
||||||
|
linkText?: TeactNode;
|
||||||
|
onLinkClick?: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ResaleGiftsNotFound = ({ className, description, linkText, onLinkClick }: OwnProps) => {
|
||||||
|
return (
|
||||||
|
<div className={buildClassName(styles.root, className)}>
|
||||||
|
<AnimatedIconWithPreview
|
||||||
|
size={160}
|
||||||
|
tgsUrl={LOCAL_TGS_URLS.SearchingDuck}
|
||||||
|
nonInteractive
|
||||||
|
noLoop
|
||||||
|
/>
|
||||||
|
<div className={styles.description}>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
{Boolean(linkText && onLinkClick) && (
|
||||||
|
<Link
|
||||||
|
className={styles.link}
|
||||||
|
onClick={onLinkClick}
|
||||||
|
>
|
||||||
|
{linkText}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ResaleGiftsNotFound);
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './GiftCraftInfoModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const GiftCraftInfoModalAsync = (props: OwnProps) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const GiftCraftInfoModal = useModuleLoader(Bundles.Stars, 'GiftCraftInfoModal', !modal);
|
||||||
|
|
||||||
|
return GiftCraftInfoModal ? <GiftCraftInfoModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(GiftCraftInfoModalAsync);
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
.header {
|
||||||
|
--_height: 16rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-self: stretch;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.understoodIcon {
|
||||||
|
font-size: 1.1875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
91
src/components/modals/gift/craft/GiftCraftInfoModal.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { memo, useMemo } from '../../../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../../../global';
|
||||||
|
|
||||||
|
import type { TabState } from '../../../../global/types';
|
||||||
|
|
||||||
|
import { getGiftAttributes } from '../../../common/helpers/gifts';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Button from '../../../ui/Button';
|
||||||
|
import TableAboutModal, { type TableAboutData } from '../../common/TableAboutModal';
|
||||||
|
import UniqueGiftHeader from '../UniqueGiftHeader';
|
||||||
|
|
||||||
|
import styles from './GiftCraftInfoModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['giftCraftInfoModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const GiftCraftInfoModal = ({
|
||||||
|
modal,
|
||||||
|
}: OwnProps) => {
|
||||||
|
const { closeGiftCraftInfoModal } = getActions();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const gift = renderingModal?.gift;
|
||||||
|
|
||||||
|
const handleClose = useLastCallback(() => {
|
||||||
|
closeGiftCraftInfoModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const giftAttributes = useMemo(() => {
|
||||||
|
return gift ? getGiftAttributes(gift) : undefined;
|
||||||
|
}, [gift]);
|
||||||
|
|
||||||
|
const header = useMemo(() => {
|
||||||
|
if (!giftAttributes) return undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UniqueGiftHeader
|
||||||
|
className={styles.header}
|
||||||
|
modelAttribute={giftAttributes.model!}
|
||||||
|
backdropAttribute={giftAttributes.backdrop!}
|
||||||
|
patternAttribute={giftAttributes.pattern!}
|
||||||
|
title={lang('GiftCraftInfoTitle')}
|
||||||
|
subtitle={lang('GiftCraftInfoSubtitle')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, [giftAttributes, lang]);
|
||||||
|
|
||||||
|
const footer = useMemo(() => {
|
||||||
|
if (!isOpen) return undefined;
|
||||||
|
return (
|
||||||
|
<div className={styles.footer}>
|
||||||
|
<Button
|
||||||
|
iconName="understood"
|
||||||
|
iconClassName={styles.understoodIcon}
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
{lang('ButtonUnderstood')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}, [lang, isOpen, handleClose]);
|
||||||
|
|
||||||
|
const listItemData = useMemo(() => {
|
||||||
|
return [
|
||||||
|
['radial-badge', lang('GiftCraftInfoCraftTitle'), lang('GiftCraftInfoCraftDescription')],
|
||||||
|
['combine-craft', lang('GiftCraftInfoChanceTitle'), lang('GiftCraftInfoChanceDescription')],
|
||||||
|
['boost-craft-chance', lang('GiftCraftInfoRiskTitle'), lang('GiftCraftInfoRiskDescription')],
|
||||||
|
] satisfies TableAboutData;
|
||||||
|
}, [lang]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableAboutModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
header={header}
|
||||||
|
listItemData={listItemData}
|
||||||
|
footer={footer}
|
||||||
|
hasBackdrop
|
||||||
|
contentClassName={styles.content}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(GiftCraftInfoModal);
|
||||||
16
src/components/modals/gift/craft/GiftCraftModal.async.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './GiftCraftModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const GiftCraftModalAsync = (props: OwnProps) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const GiftCraftModal = useModuleLoader(Bundles.Stars, 'GiftCraftModal', !modal);
|
||||||
|
|
||||||
|
return GiftCraftModal ? <GiftCraftModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(GiftCraftModalAsync);
|
||||||
722
src/components/modals/gift/craft/GiftCraftModal.module.scss
Normal file
@ -0,0 +1,722 @@
|
|||||||
|
@use "../../../../styles/mixins";
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
:global(.modal-dialog) {
|
||||||
|
overflow: clip;
|
||||||
|
background: #232E3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.modal-content) {
|
||||||
|
max-height: 92vh !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.patternOverlay {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: var(--border-radius-modal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.patternSlide,
|
||||||
|
.patternBackground {
|
||||||
|
position: absolute !important;
|
||||||
|
inset: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
align-self: flex-start;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 4.3125rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helpButton {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
top: 0.875rem;
|
||||||
|
right: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3D Cube Animation */
|
||||||
|
.cube {
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corners {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.face {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: var(--cube-size);
|
||||||
|
height: var(--cube-size);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 1.875rem;
|
||||||
|
|
||||||
|
backface-visibility: hidden;
|
||||||
|
background: #4F5B71;
|
||||||
|
|
||||||
|
transition: opacity 1s;
|
||||||
|
|
||||||
|
&.faceHidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-face="front"] {
|
||||||
|
transform: translateZ(var(--cube-half));
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-face="back"] {
|
||||||
|
transform: translateZ(calc(-1 * var(--cube-half))) rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-face="left"] {
|
||||||
|
transform: translateX(calc(-1 * var(--cube-half))) rotateY(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-face="right"] {
|
||||||
|
transform: translateX(var(--cube-half)) rotateY(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-face="top"] {
|
||||||
|
transform: translateY(calc(-1 * var(--cube-half))) rotateX(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-face="bottom"] {
|
||||||
|
transform: translateY(var(--cube-half)) rotateX(-90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.faceHighChance {
|
||||||
|
background: #36595F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faceFailed {
|
||||||
|
border-color: #653B31;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faceIcon {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftedGiftFace {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
animation: fadeInContent 0.25s ease-out 0.25s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotBackdrop,
|
||||||
|
.craftedGiftBackdrop,
|
||||||
|
.attributeRing,
|
||||||
|
.burnedGiftBackdrop {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotSticker,
|
||||||
|
.craftedGiftSticker,
|
||||||
|
.patternAttribute,
|
||||||
|
.burnedGift,
|
||||||
|
.burnedGiftSticker {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failedGiftFace {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
background: linear-gradient(180deg, #683E34 0%, #51291F 100%);
|
||||||
|
|
||||||
|
animation: fadeInContent 0.2s ease-out 0.2s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInContent {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.burnedCount {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.5rem;
|
||||||
|
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotsGrid {
|
||||||
|
--perspective: 600px;
|
||||||
|
--cube-size: 7.5rem;
|
||||||
|
--cube-half: 3.75rem;
|
||||||
|
--slot-size: 4.5rem;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--slot-size) var(--cube-size) var(--slot-size);
|
||||||
|
grid-template-rows: var(--slot-size) var(--slot-size);
|
||||||
|
gap: 1.5rem;
|
||||||
|
row-gap: 17px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
|
|
||||||
|
padding: 1.125rem;
|
||||||
|
|
||||||
|
perspective: var(--perspective);
|
||||||
|
|
||||||
|
&.activated {
|
||||||
|
.craftSlot:not(.craftSlotFilled) {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotChance,
|
||||||
|
.slotClear {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotWrapper {
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: var(--slot-size);
|
||||||
|
height: var(--slot-size);
|
||||||
|
|
||||||
|
&.bottomRow {
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.used {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cubeWrapper {
|
||||||
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transform: scale(0.913);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: var(--cube-size);
|
||||||
|
height: var(--cube-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftSlot {
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: var(--slot-size);
|
||||||
|
height: var(--slot-size);
|
||||||
|
border-radius: 1.125rem;
|
||||||
|
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
|
||||||
|
transition: transform 0.15s, background-color 0.15s, opacity 0.5s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.animating {
|
||||||
|
will-change: transform;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
.slotChance,
|
||||||
|
.slotClear {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotIcon {
|
||||||
|
font-size: 1.875rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftSlotFilled {
|
||||||
|
position: relative;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftSlotHidden {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.removing {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s, transform 0.15s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotInner {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotChance {
|
||||||
|
position: absolute;
|
||||||
|
top: -0.3125rem;
|
||||||
|
left: -0.3125rem;
|
||||||
|
|
||||||
|
padding: 0.0625rem 0.25rem;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
opacity: 0.9;
|
||||||
|
background-color: #374354B2;
|
||||||
|
backdrop-filter: blur(25px);
|
||||||
|
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotClear {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: -0.3125rem;
|
||||||
|
right: -0.3125rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
opacity: 0.9;
|
||||||
|
background-color: #374354B2;
|
||||||
|
backdrop-filter: blur(25px);
|
||||||
|
outline: none !important;
|
||||||
|
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.centerAnvil {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: var(--cube-size);
|
||||||
|
height: var(--cube-size);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 1.875rem;
|
||||||
|
|
||||||
|
background: #4F5B71;
|
||||||
|
|
||||||
|
&.centerAnvilHighChance {
|
||||||
|
background: #36595F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressRing {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anvilIcon {
|
||||||
|
font-size: 3.125rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.percentage {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.75rem;
|
||||||
|
|
||||||
|
font-size: 1.1875rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoTransition {
|
||||||
|
z-index: 1;
|
||||||
|
min-height: 20.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoSection,
|
||||||
|
.craftingSection,
|
||||||
|
.failedSection {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
padding: 0.75rem 1.5rem 1.5rem;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftingTitle {
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftingGiftName {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: #8D979B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftingWarning,
|
||||||
|
.failedDescription {
|
||||||
|
margin: 0.25rem 0 1.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftingWarning {
|
||||||
|
max-width: 15rem;
|
||||||
|
margin: 3.5rem 0 1.5rem;
|
||||||
|
color: #8D979B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failedDescription {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoDescription {
|
||||||
|
max-width: 18rem;
|
||||||
|
min-height: 2.625rem;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.giftLine {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.giftIcon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoWarning {
|
||||||
|
max-width: 18rem;
|
||||||
|
margin: 0.5rem 0 0.5rem;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributeCircles {
|
||||||
|
--circle-attribute-size: 4rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.25rem;
|
||||||
|
align-items: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
align-self: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
max-width: 17rem;
|
||||||
|
padding-top: 0.4375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllButton {
|
||||||
|
gap: 0.1875rem;
|
||||||
|
margin-top: 0.625rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllText {
|
||||||
|
margin-inline-start: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributeCircle {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
width: var(--circle-attribute-size);
|
||||||
|
|
||||||
|
@include mixins.with-vt-type('craftAttributes');
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributeContent {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: var(--circle-attribute-size);
|
||||||
|
height: var(--circle-attribute-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorAttribute {
|
||||||
|
width: 2rem;
|
||||||
|
height: 1.8125rem;
|
||||||
|
margin-top: -0.1875rem;
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line plugin/use-baseline */
|
||||||
|
mask: url('../../../../assets/attribute-mask.svg') center / 100% 100% no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patternAttributeThumb,
|
||||||
|
.burnedGiftBadge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burnedGiftBadgeText {
|
||||||
|
font-size: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributePercent {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.25rem;
|
||||||
|
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyHintIcon {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftButton {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 3.5rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
|
||||||
|
&:global(.danger) {
|
||||||
|
color: var(--color-white);
|
||||||
|
background-color: var(--color-error);
|
||||||
|
|
||||||
|
&:not(.disabled):not(:disabled) {
|
||||||
|
&:active,
|
||||||
|
&:global(.active),
|
||||||
|
&:focus {
|
||||||
|
opacity: 0.85 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.85 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftButtonCrafting {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: rgba(255, 255, 255, 0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftButtonHighChance {
|
||||||
|
background: linear-gradient(90deg, #0A8A90, #34C351) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftButtonContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.125rem;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftButtonTitle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftButtonSubtitle {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.failedTitle {
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burnedGifts {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burnedGiftInner {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 4.25rem;
|
||||||
|
height: 4.25rem;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mixins.on-active-vt('craftAttributes') {
|
||||||
|
&::view-transition-group(.craftAttribute) {
|
||||||
|
animation-duration: 250ms;
|
||||||
|
animation-timing-function: ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::view-transition-old(.craftAttribute) {
|
||||||
|
:local {
|
||||||
|
animation-name: craftAttributeFadeOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes craftAttributeFadeOut {
|
||||||
|
to {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::view-transition-new(.craftAttribute) {
|
||||||
|
:local {
|
||||||
|
animation-name: craftAttributeFadeIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes craftAttributeFadeIn {
|
||||||
|
from {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1644
src/components/modals/gift/craft/GiftCraftModal.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './GiftCraftSelectModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const GiftCraftSelectModalAsync = (props: OwnProps) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const GiftCraftSelectModal = useModuleLoader(Bundles.Stars, 'GiftCraftSelectModal', !modal);
|
||||||
|
|
||||||
|
return GiftCraftSelectModal ? <GiftCraftSelectModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(GiftCraftSelectModalAsync);
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
.modal {
|
||||||
|
:global(.modal-dialog) {
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.modal-content) {
|
||||||
|
height: min(92vh, 39rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem 1rem 0.5rem;
|
||||||
|
border-bottom: 0.0625rem solid transparent;
|
||||||
|
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
transition: border-bottom-color 0.15s ease;
|
||||||
|
|
||||||
|
&.titleWithBorder {
|
||||||
|
border-bottom-color: var(--color-borders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollContainer {
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 1rem 1rem;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: var(--color-primary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
position: sticky;
|
||||||
|
z-index: 2;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
padding-block: 0.5rem;
|
||||||
|
border-bottom: 0.0625rem solid transparent;
|
||||||
|
|
||||||
|
background-color: var(--color-background);
|
||||||
|
|
||||||
|
transition: border-bottom-color 0.15s ease;
|
||||||
|
|
||||||
|
&.stuck {
|
||||||
|
border-bottom-color: var(--color-borders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.transitionWrapper {
|
||||||
|
z-index: 0;
|
||||||
|
min-height: 28rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.giftsGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-content: start;
|
||||||
|
|
||||||
|
min-height: 28rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.giftWrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.giftChance {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.25rem;
|
||||||
|
left: 0.25rem;
|
||||||
|
|
||||||
|
padding: 0.0625rem 0.25rem;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notFound {
|
||||||
|
padding-top: 5rem;
|
||||||
|
padding-bottom: 12rem;
|
||||||
|
}
|
||||||
367
src/components/modals/gift/craft/GiftCraftSelectModal.tsx
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
import {
|
||||||
|
memo, useMemo, useRef, useState,
|
||||||
|
} from '../../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
|
import type { ApiSavedStarGift, ApiStarGift, ApiStarGiftUnique } from '../../../../api/types';
|
||||||
|
import type { TabState } from '../../../../global/types';
|
||||||
|
import type { ObserveFn } from '../../../../hooks/useIntersectionObserver';
|
||||||
|
|
||||||
|
import { getSavedGiftKey } from '../../../../global/helpers/stars';
|
||||||
|
import { selectTabState } from '../../../../global/selectors';
|
||||||
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
|
import { throttle } from '../../../../util/schedulers';
|
||||||
|
import { formatPercent } from '../../../../util/textFormat';
|
||||||
|
import { getGiftAttributes } from '../../../common/helpers/gifts';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||||
|
import useInfiniteScroll from '../../../../hooks/useInfiniteScroll';
|
||||||
|
import { useIntersectionObserver } from '../../../../hooks/useIntersectionObserver';
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
import usePrevious from '../../../../hooks/usePrevious';
|
||||||
|
|
||||||
|
import InfiniteScroll from '../../../ui/InfiniteScroll';
|
||||||
|
import Loading from '../../../ui/Loading';
|
||||||
|
import Modal from '../../../ui/Modal';
|
||||||
|
import Transition from '../../../ui/Transition';
|
||||||
|
import GiftItemStar from '../GiftItemStar';
|
||||||
|
import GiftResaleFilters from '../GiftResaleFilters';
|
||||||
|
import ResaleGiftsNotFound from '../ResaleGiftsNotFound';
|
||||||
|
|
||||||
|
import styles from './GiftCraftSelectModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['giftCraftSelectModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
craftModal?: TabState['giftCraftModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const CRAFT_GIFTS_LIMIT = 50;
|
||||||
|
const INTERSECTION_THROTTLE = 200;
|
||||||
|
const SCROLL_THROTTLE = 200;
|
||||||
|
|
||||||
|
const runThrottledForScroll = throttle((cb: NoneToVoidFunction) => cb(), SCROLL_THROTTLE, true);
|
||||||
|
|
||||||
|
type CraftGiftItemProps = {
|
||||||
|
gift: ApiStarGift;
|
||||||
|
chancePercent?: number;
|
||||||
|
chanceColor?: string;
|
||||||
|
showPrice?: boolean;
|
||||||
|
observe?: ObserveFn;
|
||||||
|
onClick: (gift: ApiStarGift) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CraftGiftItem = memo(({ gift, chancePercent, chanceColor, showPrice, observe, onClick }: CraftGiftItemProps) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.giftWrapper}>
|
||||||
|
<GiftItemStar
|
||||||
|
gift={gift}
|
||||||
|
observeIntersection={observe}
|
||||||
|
hideBadge={!showPrice}
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
{Boolean(chancePercent && chancePercent > 0) && (
|
||||||
|
<span
|
||||||
|
className={styles.giftChance}
|
||||||
|
style={chanceColor ? `background-color: ${chanceColor}` : undefined}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
{formatPercent(chancePercent!, 0)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const GiftCraftSelectModal = ({ modal, craftModal }: OwnProps & StateProps) => {
|
||||||
|
const {
|
||||||
|
closeGiftCraftSelectModal, selectGiftForCraft,
|
||||||
|
loadMoreCraftableGifts, loadMoreMarketCraftableGifts,
|
||||||
|
updateCraftGiftsFilter, openGiftInfoModal,
|
||||||
|
} = getActions();
|
||||||
|
|
||||||
|
const scrollerRef = useRef<HTMLDivElement>();
|
||||||
|
const dialogRef = useRef<HTMLDivElement>();
|
||||||
|
const filtersSeparatorRef = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
const [isFiltersSeparatorAbove, setIsFiltersSeparatorAbove] = useState(false);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const renderingCraftModal = useCurrentOrPrev(craftModal);
|
||||||
|
|
||||||
|
const {
|
||||||
|
gift1,
|
||||||
|
gift2,
|
||||||
|
gift3,
|
||||||
|
gift4,
|
||||||
|
myCraftableGifts,
|
||||||
|
marketCraftableGifts,
|
||||||
|
marketFilter,
|
||||||
|
marketUpdateIteration = 0,
|
||||||
|
marketCraftableGiftsCount,
|
||||||
|
myCraftableGiftsNextOffset,
|
||||||
|
marketCraftableGiftsNextOffset,
|
||||||
|
isMarketLoading,
|
||||||
|
} = renderingCraftModal || {};
|
||||||
|
|
||||||
|
const { isLoading } = renderingModal || {};
|
||||||
|
|
||||||
|
const hasMoreMyGifts = Boolean(myCraftableGiftsNextOffset);
|
||||||
|
const hasMoreMarketGifts = Boolean(marketCraftableGiftsNextOffset);
|
||||||
|
|
||||||
|
const { observe } = useIntersectionObserver({
|
||||||
|
rootRef: scrollerRef,
|
||||||
|
throttleMs: INTERSECTION_THROTTLE,
|
||||||
|
isDisabled: !isOpen,
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasMarketGiftsData = Boolean(marketCraftableGifts?.length);
|
||||||
|
const isMyDataLoaded = myCraftableGifts !== undefined;
|
||||||
|
const isMarketDataLoaded = marketCraftableGifts !== undefined;
|
||||||
|
const isDataLoaded = isMyDataLoaded && isMarketDataLoaded && !isLoading;
|
||||||
|
const prevMarketIteration = usePrevious(marketUpdateIteration);
|
||||||
|
const isMarketJustUpdated = prevMarketIteration !== undefined && prevMarketIteration !== marketUpdateIteration;
|
||||||
|
const isFiltersStuck = isScrolled && isFiltersSeparatorAbove;
|
||||||
|
const shouldShowTitleBorder = isScrolled && !isFiltersStuck;
|
||||||
|
|
||||||
|
const handleScroll = useLastCallback((e: { currentTarget: HTMLDivElement }) => {
|
||||||
|
const scroller = e.currentTarget;
|
||||||
|
|
||||||
|
runThrottledForScroll(() => {
|
||||||
|
setIsScrolled(scroller.scrollTop > 0);
|
||||||
|
|
||||||
|
const separator = filtersSeparatorRef.current;
|
||||||
|
if (separator && hasMarketGiftsData) {
|
||||||
|
const scrollerRect = scroller.getBoundingClientRect();
|
||||||
|
const separatorRect = separator.getBoundingClientRect();
|
||||||
|
setIsFiltersSeparatorAbove(separatorRect.top <= scrollerRect.top);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedIds = useMemo(() => {
|
||||||
|
return new Set(
|
||||||
|
[gift1, gift2, gift3, gift4]
|
||||||
|
.filter((g): g is ApiSavedStarGift => Boolean(g))
|
||||||
|
.map((g) => getSavedGiftKey(g)),
|
||||||
|
);
|
||||||
|
}, [gift1, gift2, gift3, gift4]);
|
||||||
|
|
||||||
|
const selectedUniqueIds = useMemo(() => {
|
||||||
|
return new Set(
|
||||||
|
[gift1, gift2, gift3, gift4]
|
||||||
|
.filter((g): g is ApiSavedStarGift => Boolean(g) && g.gift.type === 'starGiftUnique')
|
||||||
|
.map((g) => (g.gift as ApiStarGiftUnique).id),
|
||||||
|
);
|
||||||
|
}, [gift1, gift2, gift3, gift4]);
|
||||||
|
|
||||||
|
const availableMyGifts = useMemo(() => {
|
||||||
|
if (!myCraftableGifts) return [];
|
||||||
|
return myCraftableGifts.filter((g) => {
|
||||||
|
if (g.gift.type === 'starGiftUnique' && selectedUniqueIds.has(g.gift.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !selectedIds.has(getSavedGiftKey(g));
|
||||||
|
});
|
||||||
|
}, [myCraftableGifts, selectedIds, selectedUniqueIds]);
|
||||||
|
|
||||||
|
const availableMarketGifts = useMemo(() => {
|
||||||
|
if (!marketCraftableGifts) return [];
|
||||||
|
return marketCraftableGifts.filter((g) => !selectedUniqueIds.has(g.id));
|
||||||
|
}, [marketCraftableGifts, selectedUniqueIds]);
|
||||||
|
|
||||||
|
const myGiftByIdMap = useMemo(() => {
|
||||||
|
const map = new Map<string, ApiSavedStarGift>();
|
||||||
|
availableMyGifts.forEach((g) => {
|
||||||
|
if (g.gift.type === 'starGiftUnique') {
|
||||||
|
map.set(g.gift.id, g);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}, [availableMyGifts]);
|
||||||
|
|
||||||
|
const handleLoadMore = useLastCallback(() => {
|
||||||
|
if (hasMoreMyGifts) {
|
||||||
|
loadMoreCraftableGifts();
|
||||||
|
} else if (hasMoreMarketGifts) {
|
||||||
|
loadMoreMarketCraftableGifts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const allItemIds = useMemo(() => {
|
||||||
|
const myIds = availableMyGifts.map((g) => `my-${getSavedGiftKey(g)}`);
|
||||||
|
const marketIds = availableMarketGifts.map((g) => `market-${g.id}`);
|
||||||
|
return [...myIds, ...marketIds];
|
||||||
|
}, [availableMyGifts, availableMarketGifts]);
|
||||||
|
|
||||||
|
const [viewportIds, getMore] = useInfiniteScroll(
|
||||||
|
handleLoadMore,
|
||||||
|
allItemIds,
|
||||||
|
!isOpen,
|
||||||
|
CRAFT_GIFTS_LIMIT,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClose = useLastCallback(() => {
|
||||||
|
closeGiftCraftSelectModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleMyGiftClick = useLastCallback((gift: ApiStarGift) => {
|
||||||
|
if (gift.type !== 'starGiftUnique') return;
|
||||||
|
const savedGift = myGiftByIdMap.get(gift.id);
|
||||||
|
const slotIndex = renderingModal?.slotIndex;
|
||||||
|
if (savedGift && slotIndex !== undefined) {
|
||||||
|
selectGiftForCraft({ gift: savedGift, slotIndex });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleMarketGiftClick = useLastCallback((gift: ApiStarGift) => {
|
||||||
|
const slotIndex = renderingModal?.slotIndex;
|
||||||
|
if (slotIndex === undefined) return;
|
||||||
|
|
||||||
|
openGiftInfoModal({ gift, craftSlotIndex: slotIndex });
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleResetMarketFilter = useLastCallback(() => {
|
||||||
|
updateCraftGiftsFilter({
|
||||||
|
filter: {
|
||||||
|
sortType: marketFilter?.sortType || 'byPrice',
|
||||||
|
modelAttributes: [],
|
||||||
|
backdropAttributes: [],
|
||||||
|
patternAttributes: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasMyGifts = availableMyGifts.length > 0;
|
||||||
|
const hasMarketGifts = availableMarketGifts.length > 0;
|
||||||
|
const isMarketGiftsEmpty = !hasMarketGifts && Boolean(marketCraftableGifts);
|
||||||
|
const hasMarketFilter = Boolean(
|
||||||
|
marketFilter?.modelAttributes?.length
|
||||||
|
|| marketFilter?.patternAttributes?.length
|
||||||
|
|| marketFilter?.backdropAttributes?.length,
|
||||||
|
);
|
||||||
|
const shouldShowMarketSection = isMarketDataLoaded && (hasMarketGifts || hasMarketFilter || isMarketLoading);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
dialogRef={dialogRef}
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={handleClose}
|
||||||
|
className={styles.modal}
|
||||||
|
contentClassName={styles.content}
|
||||||
|
hasAbsoluteCloseButton
|
||||||
|
isSlim
|
||||||
|
isLowStackPriority
|
||||||
|
>
|
||||||
|
<h3 className={buildClassName(styles.title, shouldShowTitleBorder && styles.titleWithBorder)}>
|
||||||
|
{lang('GiftCraftSelectTitle')}
|
||||||
|
</h3>
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<Loading className={buildClassName(styles.loading, !isLoading && styles.hidden)} />
|
||||||
|
<InfiniteScroll
|
||||||
|
ref={scrollerRef}
|
||||||
|
className={buildClassName(styles.scrollContainer, isLoading && styles.hidden, 'custom-scroll')}
|
||||||
|
items={viewportIds}
|
||||||
|
onLoadMore={getMore}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
itemSelector=".starGiftItem"
|
||||||
|
noFastList
|
||||||
|
noScrollRestore={isMarketJustUpdated}
|
||||||
|
preloadBackwards={CRAFT_GIFTS_LIMIT}
|
||||||
|
>
|
||||||
|
{isOpen && !hasMyGifts && !shouldShowMarketSection && isDataLoaded && (
|
||||||
|
<ResaleGiftsNotFound
|
||||||
|
className={styles.notFound}
|
||||||
|
description={lang('ResellGiftsNoFound')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isOpen && hasMyGifts && (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<p className={styles.sectionTitle}>{lang('GiftCraftSelectYourGifts')}</p>
|
||||||
|
<div className={styles.giftsGrid}>
|
||||||
|
{availableMyGifts.map((savedGift) => {
|
||||||
|
const chancePercent = savedGift.gift.type === 'starGiftUnique'
|
||||||
|
? (savedGift.gift.craftChancePermille || 0) / 10
|
||||||
|
: 0;
|
||||||
|
const { backdrop } = getGiftAttributes(savedGift.gift) || {};
|
||||||
|
return (
|
||||||
|
<CraftGiftItem
|
||||||
|
key={`my-${getSavedGiftKey(savedGift)}`}
|
||||||
|
gift={savedGift.gift}
|
||||||
|
chancePercent={chancePercent}
|
||||||
|
chanceColor={backdrop?.centerColor}
|
||||||
|
observe={observe}
|
||||||
|
onClick={handleMyGiftClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isOpen && shouldShowMarketSection && (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<p className={styles.sectionTitle}>
|
||||||
|
{lang('GiftCraftSelectMarketGifts', {
|
||||||
|
count: marketCraftableGiftsCount || 0 },
|
||||||
|
{ pluralValue: marketCraftableGiftsCount || 0 })}
|
||||||
|
</p>
|
||||||
|
<div ref={filtersSeparatorRef} />
|
||||||
|
<GiftResaleFilters
|
||||||
|
dialogRef={dialogRef}
|
||||||
|
className={buildClassName(styles.filters, isFiltersStuck && styles.stuck)}
|
||||||
|
filterType="craft"
|
||||||
|
/>
|
||||||
|
<Transition
|
||||||
|
className={styles.transitionWrapper}
|
||||||
|
name="semiFade"
|
||||||
|
activeKey={marketUpdateIteration}
|
||||||
|
>
|
||||||
|
{isMarketGiftsEmpty && (
|
||||||
|
<ResaleGiftsNotFound
|
||||||
|
className={styles.notFound}
|
||||||
|
description={lang('ResellGiftsNoFound')}
|
||||||
|
linkText={hasMarketFilter ? lang('ResellGiftsClearFilters') : undefined}
|
||||||
|
onLinkClick={hasMarketFilter ? handleResetMarketFilter : undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{hasMarketGifts && (
|
||||||
|
<div className={styles.giftsGrid}>
|
||||||
|
{availableMarketGifts.map((marketGift) => {
|
||||||
|
const chancePercent = (marketGift.craftChancePermille || 0) / 10;
|
||||||
|
const { backdrop } = getGiftAttributes(marketGift) || {};
|
||||||
|
return (
|
||||||
|
<CraftGiftItem
|
||||||
|
key={`market-${marketGift.id}`}
|
||||||
|
gift={marketGift}
|
||||||
|
chancePercent={chancePercent}
|
||||||
|
chanceColor={backdrop?.centerColor}
|
||||||
|
showPrice
|
||||||
|
observe={observe}
|
||||||
|
onClick={handleMarketGiftClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</InfiniteScroll>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>((global): Complete<StateProps> => {
|
||||||
|
const tabState = selectTabState(global);
|
||||||
|
|
||||||
|
return {
|
||||||
|
craftModal: tabState.giftCraftModal,
|
||||||
|
};
|
||||||
|
})(GiftCraftSelectModal));
|
||||||
16
src/components/modals/gift/craft/RadialProgress.module.scss
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.root {
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
stroke: rgba(255, 255, 255, 0.15);
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-width: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill {
|
||||||
|
stroke: white;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-width: 0.375rem;
|
||||||
|
transition: stroke-dashoffset 0.3s ease;
|
||||||
|
}
|
||||||
61
src/components/modals/gift/craft/RadialProgress.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
|
import { clamp } from '../../../../util/math';
|
||||||
|
import { REM } from '../../../common/helpers/mediaDimensions';
|
||||||
|
|
||||||
|
import styles from './RadialProgress.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
progress: number;
|
||||||
|
size?: number;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const VIEWBOX_SIZE = 100;
|
||||||
|
const RADIUS_RATIO = 0.35; // 35% of viewbox size
|
||||||
|
const STROKE_START = 0.125;
|
||||||
|
const STROKE_END = 0.875;
|
||||||
|
const ARC_RANGE = STROKE_END - STROKE_START; // 0.75 = 270 degrees
|
||||||
|
|
||||||
|
export const DEFAULT_RING_SIZE = 7.5 * REM;
|
||||||
|
|
||||||
|
const RadialProgress = ({ progress, size = DEFAULT_RING_SIZE, className }: OwnProps) => {
|
||||||
|
const center = VIEWBOX_SIZE / 2;
|
||||||
|
const radius = VIEWBOX_SIZE * RADIUS_RATIO;
|
||||||
|
|
||||||
|
const clampedProgress = clamp(progress, 0, 100);
|
||||||
|
const progressStrokeEnd = STROKE_START + ARC_RANGE * (clampedProgress / 100);
|
||||||
|
|
||||||
|
const bgDashoffset = 1 - ARC_RANGE;
|
||||||
|
const fillDashoffset = 1 - (progressStrokeEnd - STROKE_START);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={buildClassName(styles.root, className)}
|
||||||
|
viewBox={`0 0 ${VIEWBOX_SIZE} ${VIEWBOX_SIZE}`}
|
||||||
|
style={`width: ${size}px; height: ${size}px`}
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
className={styles.background}
|
||||||
|
cx={center}
|
||||||
|
cy={center}
|
||||||
|
r={radius}
|
||||||
|
fill="none"
|
||||||
|
pathLength="1"
|
||||||
|
style={`stroke-dasharray: 1; stroke-dashoffset: ${bgDashoffset}`}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
className={styles.fill}
|
||||||
|
cx={center}
|
||||||
|
cy={center}
|
||||||
|
r={radius}
|
||||||
|
fill="none"
|
||||||
|
pathLength="1"
|
||||||
|
style={`stroke-dasharray: 1; stroke-dashoffset: ${fillDashoffset}`}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(RadialProgress);
|
||||||
@ -2,6 +2,21 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.headerRightButtons {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
top: 0.875rem;
|
||||||
|
right: 3rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.craftButton {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.uniqueTitleNumber {
|
.uniqueTitleNumber {
|
||||||
&.small {
|
&.small {
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
@ -73,9 +88,7 @@
|
|||||||
.giftResalePriceContainer {
|
.giftResalePriceContainer {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 0.875rem;
|
|
||||||
right: 3.25rem;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import { selectPeer, selectUser } from '../../../../global/selectors';
|
|||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
import { copyTextToClipboard } from '../../../../util/clipboard';
|
import { copyTextToClipboard } from '../../../../util/clipboard';
|
||||||
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
||||||
import { formatCurrencyAsString } from '../../../../util/formatCurrency';
|
import { formatCurrency, formatCurrencyAsString } from '../../../../util/formatCurrency';
|
||||||
import {
|
import {
|
||||||
formatStarsAsIcon, formatStarsAsText, formatTonAsIcon, formatTonAsText,
|
formatStarsAsIcon, formatStarsAsText, formatTonAsIcon, formatTonAsText,
|
||||||
getNextArrowReplacement,
|
getNextArrowReplacement,
|
||||||
@ -91,6 +91,7 @@ const GiftInfoModal = ({
|
|||||||
openChatWithInfo,
|
openChatWithInfo,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
openGiftUpgradeModal,
|
openGiftUpgradeModal,
|
||||||
|
openGiftCraftModal,
|
||||||
showNotification,
|
showNotification,
|
||||||
buyStarGift,
|
buyStarGift,
|
||||||
closeGiftModal,
|
closeGiftModal,
|
||||||
@ -247,6 +248,12 @@ const GiftInfoModal = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleOpenCraftModal = useLastCallback(() => {
|
||||||
|
if (!savedGift || savedGift.gift.type !== 'starGiftUnique') return;
|
||||||
|
handleClose();
|
||||||
|
openGiftCraftModal({ gift: savedGift });
|
||||||
|
});
|
||||||
|
|
||||||
const giftAttributes = useMemo(() => {
|
const giftAttributes = useMemo(() => {
|
||||||
return gift && getGiftAttributes(gift);
|
return gift && getGiftAttributes(gift);
|
||||||
}, [gift]);
|
}, [gift]);
|
||||||
@ -262,6 +269,7 @@ const GiftInfoModal = ({
|
|||||||
if (!gift || gift.type !== 'starGiftUnique' || !giftAttributes?.backdrop) return undefined;
|
if (!gift || gift.type !== 'starGiftUnique' || !giftAttributes?.backdrop) return undefined;
|
||||||
|
|
||||||
const numberColor = giftAttributes.backdrop.textColor;
|
const numberColor = giftAttributes.backdrop.textColor;
|
||||||
|
|
||||||
const digitCount = String(gift.number).length;
|
const digitCount = String(gift.number).length;
|
||||||
const numberSizeClass = digitCount >= 6 ? styles.small : styles.regular;
|
const numberSizeClass = digitCount >= 6 ? styles.small : styles.regular;
|
||||||
const styledNumber = (
|
const styledNumber = (
|
||||||
@ -300,9 +308,7 @@ const GiftInfoModal = ({
|
|||||||
<Button className={styles.buyButton} onClick={handleBuyGift}>
|
<Button className={styles.buyButton} onClick={handleBuyGift}>
|
||||||
<div>
|
<div>
|
||||||
{lang('ButtonBuyGift', {
|
{lang('ButtonBuyGift', {
|
||||||
stars: resellPrice?.currency === TON_CURRENCY_CODE
|
stars: formatCurrency(lang, resellPrice.amount, resellPrice.currency, { asFontIcon: true }),
|
||||||
? formatTonAsIcon(lang, resellPrice.amount, { shouldConvertFromNanos: true })
|
|
||||||
: formatStarsAsIcon(lang, resellPrice?.amount, { asFont: true }),
|
|
||||||
}, { withNodes: true })}
|
}, { withNodes: true })}
|
||||||
</div>
|
</div>
|
||||||
{resellPrice?.currency === TON_CURRENCY_CODE && Boolean(resellPriceInStars) && (
|
{resellPrice?.currency === TON_CURRENCY_CODE && Boolean(resellPriceInStars) && (
|
||||||
@ -389,6 +395,14 @@ const GiftInfoModal = ({
|
|||||||
return text;
|
return text;
|
||||||
}, [gift, lang]);
|
}, [gift, lang]);
|
||||||
|
|
||||||
|
// ToDo
|
||||||
|
// const canCraft = Boolean(
|
||||||
|
// canManage && savedGift?.canCraftAt && getServerTime() >= savedGift.canCraftAt,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// Mock for Tests
|
||||||
|
const canCraft = Boolean(canManage && savedGift?.canCraftAt);
|
||||||
|
|
||||||
const modalData = useMemo(() => {
|
const modalData = useMemo(() => {
|
||||||
if (!typeGift || !gift) {
|
if (!typeGift || !gift) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -487,10 +501,7 @@ const GiftInfoModal = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uniqueGiftModalHeader = (
|
const uniqueGiftModalHeader = (
|
||||||
<div
|
<div className={styles.modalHeader}>
|
||||||
className={styles.modalHeader}
|
|
||||||
>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className={styles.closeButton}
|
className={styles.closeButton}
|
||||||
round
|
round
|
||||||
@ -500,23 +511,34 @@ const GiftInfoModal = ({
|
|||||||
ariaLabel={lang('Close')}
|
ariaLabel={lang('Close')}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{Boolean(resellPrice?.amount) && (
|
|
||||||
<div className={styles.giftResalePriceContainer}>
|
|
||||||
{resellPrice.currency === TON_CURRENCY_CODE
|
|
||||||
? formatTonAsIcon(lang, resellPrice.amount, {
|
|
||||||
className: styles.giftResalePriceStar,
|
|
||||||
shouldConvertFromNanos: true,
|
|
||||||
})
|
|
||||||
: formatStarsAsIcon(lang, resellPrice.amount, {
|
|
||||||
asFont: true,
|
|
||||||
className: styles.giftResalePriceStar,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const headerRightToolBar = (Boolean(resellPrice?.amount) || canCraft) ? (
|
||||||
|
<div className={styles.headerRightButtons}>
|
||||||
|
{Boolean(resellPrice?.amount) && (
|
||||||
|
<div className={styles.giftResalePriceContainer}>
|
||||||
|
{formatCurrency(lang, resellPrice.amount, resellPrice.currency, {
|
||||||
|
asFontIcon: true,
|
||||||
|
iconClassName: styles.giftResalePriceStar,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{canCraft && (
|
||||||
|
<Button
|
||||||
|
className={styles.craftButton}
|
||||||
|
round
|
||||||
|
color="translucent-white"
|
||||||
|
size="tiny"
|
||||||
|
ariaLabel={lang('GiftInfoCraft')}
|
||||||
|
onClick={handleOpenCraftModal}
|
||||||
|
>
|
||||||
|
<Icon name="craft" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : undefined;
|
||||||
|
|
||||||
const uniqueGiftHeader = isGiftUnique && (
|
const uniqueGiftHeader = isGiftUnique && (
|
||||||
<div ref={uniqueGiftHeaderRef} className={buildClassName(styles.header, styles.uniqueGift)}>
|
<div ref={uniqueGiftHeaderRef} className={buildClassName(styles.header, styles.uniqueGift)}>
|
||||||
<UniqueGiftHeader
|
<UniqueGiftHeader
|
||||||
@ -804,6 +826,7 @@ const GiftInfoModal = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
modalHeader: isGiftUnique ? uniqueGiftModalHeader : undefined,
|
modalHeader: isGiftUnique ? uniqueGiftModalHeader : undefined,
|
||||||
|
headerRightToolBar: isGiftUnique ? headerRightToolBar : undefined,
|
||||||
header: isGiftUnique ? uniqueGiftHeader : regularHeader,
|
header: isGiftUnique ? uniqueGiftHeader : regularHeader,
|
||||||
tableData,
|
tableData,
|
||||||
footer,
|
footer,
|
||||||
@ -812,7 +835,7 @@ const GiftInfoModal = ({
|
|||||||
typeGift, savedGift, renderingTargetPeer, giftSticker, lang,
|
typeGift, savedGift, renderingTargetPeer, giftSticker, lang,
|
||||||
canManage, hasConvertOption, isSender, oldLang, tonExplorerUrl,
|
canManage, hasConvertOption, isSender, oldLang, tonExplorerUrl,
|
||||||
gift, giftAttributes, renderFooterButton, isTargetChat,
|
gift, giftAttributes, renderFooterButton, isTargetChat,
|
||||||
isGiftUnique, saleDateInfo,
|
isGiftUnique, saleDateInfo, canCraft, handleOpenCraftModal,
|
||||||
canBuyGift, giftOwnerTitle, resellPrice, uniqueGiftTitle, uniqueGiftSubtitle, releasedByPeer,
|
canBuyGift, giftOwnerTitle, resellPrice, uniqueGiftTitle, uniqueGiftSubtitle, releasedByPeer,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -831,6 +854,7 @@ const GiftInfoModal = ({
|
|||||||
<TableInfoModal
|
<TableInfoModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
modalHeader={modalData?.modalHeader}
|
modalHeader={modalData?.modalHeader}
|
||||||
|
headerRightToolBar={modalData?.headerRightToolBar}
|
||||||
header={modalData?.header}
|
header={modalData?.header}
|
||||||
hasBackdrop={isGiftUnique}
|
hasBackdrop={isGiftUnique}
|
||||||
tableData={modalData?.tableData}
|
tableData={modalData?.tableData}
|
||||||
@ -842,7 +866,7 @@ const GiftInfoModal = ({
|
|||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
withBalanceBar={Boolean(canBuyGift)}
|
withBalanceBar={Boolean(canBuyGift)}
|
||||||
currencyInBalanceBar={confirmPrice?.currency}
|
currencyInBalanceBar={confirmPrice?.currency}
|
||||||
isLowStackPriority
|
isLowStackPriority={renderingModal?.craftSlotIndex !== undefined ? true : undefined}
|
||||||
/>
|
/>
|
||||||
{uniqueGift && currentUser && Boolean(confirmPrice) && (
|
{uniqueGift && currentUser && Boolean(confirmPrice) && (
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
|||||||
@ -123,11 +123,20 @@ const GiftPreviewModal = ({ modal, animationLevel }: OwnProps & StateProps) => {
|
|||||||
if (newModel && newModel.rarity.type !== 'regular') showCraftableModels();
|
if (newModel && newModel.rarity.type !== 'regular') showCraftableModels();
|
||||||
}, [initialAttributes, firstModel, firstPattern, firstBackdrop]);
|
}, [initialAttributes, firstModel, firstPattern, firstBackdrop]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (renderingModal?.shouldShowCraftableOnStart) {
|
||||||
|
showCraftableModels();
|
||||||
|
}
|
||||||
|
}, [renderingModal?.shouldShowCraftableOnStart]);
|
||||||
|
|
||||||
const handleStickerAnimationEnded = useLastCallback((modelName: string) => {
|
const handleStickerAnimationEnded = useLastCallback((modelName: string) => {
|
||||||
if (modelName !== selectedModel?.name || !isPlayingRandomPreviews) return;
|
if (modelName !== selectedModel?.name || !isPlayingRandomPreviews) return;
|
||||||
|
|
||||||
if (!originGift || !selectedModel || !selectedPattern || !selectedBackdrop) return;
|
if (!originGift || !selectedModel || !selectedPattern || !selectedBackdrop) return;
|
||||||
const newAttributes = getRandomGiftPreviewAttributes(renderingModal?.attributes, {
|
const attributesToUse = renderingModal?.shouldShowCraftableOnStart && isCraftableModelsMode
|
||||||
|
? renderingModal.attributes.filter((attr) => attr.type !== 'model' || attr.rarity.type !== 'regular')
|
||||||
|
: renderingModal?.attributes;
|
||||||
|
const newAttributes = getRandomGiftPreviewAttributes(attributesToUse, {
|
||||||
model: selectedModel,
|
model: selectedModel,
|
||||||
pattern: selectedPattern,
|
pattern: selectedPattern,
|
||||||
backdrop: selectedBackdrop,
|
backdrop: selectedBackdrop,
|
||||||
|
|||||||
@ -47,6 +47,7 @@ export type OwnProps = {
|
|||||||
isLowStackPriority?: boolean;
|
isLowStackPriority?: boolean;
|
||||||
dialogContent?: React.ReactNode;
|
dialogContent?: React.ReactNode;
|
||||||
moreMenuItems?: TeactNode;
|
moreMenuItems?: TeactNode;
|
||||||
|
headerRightToolBar?: TeactNode;
|
||||||
withBalanceBar?: boolean;
|
withBalanceBar?: boolean;
|
||||||
currencyInBalanceBar?: 'TON' | 'XTR';
|
currencyInBalanceBar?: 'TON' | 'XTR';
|
||||||
isCondensedHeader?: boolean;
|
isCondensedHeader?: boolean;
|
||||||
@ -95,6 +96,7 @@ const Modal = (props: OwnProps) => {
|
|||||||
dialogStyle,
|
dialogStyle,
|
||||||
dialogContent,
|
dialogContent,
|
||||||
moreMenuItems,
|
moreMenuItems,
|
||||||
|
headerRightToolBar: headerToolBar,
|
||||||
withBalanceBar,
|
withBalanceBar,
|
||||||
isCondensedHeader,
|
isCondensedHeader,
|
||||||
currencyInBalanceBar = 'XTR',
|
currencyInBalanceBar = 'XTR',
|
||||||
@ -226,6 +228,7 @@ const Modal = (props: OwnProps) => {
|
|||||||
)}
|
)}
|
||||||
<div className={modalDialogClassName} ref={actualDialogRef} style={dialogStyle}>
|
<div className={modalDialogClassName} ref={actualDialogRef} style={dialogStyle}>
|
||||||
{renderHeader()}
|
{renderHeader()}
|
||||||
|
{headerToolBar}
|
||||||
{Boolean(moreMenuItems) && (
|
{Boolean(moreMenuItems) && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import type { ApiSavedStarGift, ApiStarGiftUnique } from '../../../api/types';
|
import type {
|
||||||
|
ApiInputSavedStarGift,
|
||||||
|
ApiRequestInputSavedStarGift,
|
||||||
|
ApiSavedStarGift,
|
||||||
|
ApiStarGiftAttribute,
|
||||||
|
ApiStarGiftUnique,
|
||||||
|
} from '../../../api/types';
|
||||||
import type { ActionReturnType } from '../../types';
|
import type { ActionReturnType } from '../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -10,6 +16,7 @@ import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
|||||||
import { buildCollectionByCallback, buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByCallback, buildCollectionByKey } from '../../../util/iteratees';
|
||||||
import { getServerTime } from '../../../util/serverTime';
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
|
import { preloadGiftAttributeStickers } from '../../../components/common/helpers/gifts';
|
||||||
import { RESALE_GIFTS_LIMIT } from '../../../limits';
|
import { RESALE_GIFTS_LIMIT } from '../../../limits';
|
||||||
import { areInputSavedGiftsEqual, getRequestInputSavedStarGift } from '../../helpers/payments';
|
import { areInputSavedGiftsEqual, getRequestInputSavedStarGift } from '../../helpers/payments';
|
||||||
import { addActionHandler, getGlobal, getPromiseActions, setGlobal } from '../../index';
|
import { addActionHandler, getGlobal, getPromiseActions, setGlobal } from '../../index';
|
||||||
@ -27,6 +34,8 @@ import {
|
|||||||
import { updateTabState } from '../../reducers/tabs';
|
import { updateTabState } from '../../reducers/tabs';
|
||||||
import {
|
import {
|
||||||
selectActiveGiftsCollectionId,
|
selectActiveGiftsCollectionId,
|
||||||
|
selectChat,
|
||||||
|
selectChatMessage,
|
||||||
selectGiftProfileFilter,
|
selectGiftProfileFilter,
|
||||||
selectPeer,
|
selectPeer,
|
||||||
selectPeerCollectionSavedGifts,
|
selectPeerCollectionSavedGifts,
|
||||||
@ -753,8 +762,389 @@ addActionHandler('loadActiveGiftAuctions', async (global, actions, payload): Pro
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('openGiftInfoModalFromMessage', async (global, actions, payload): Promise<void> => {
|
||||||
|
const {
|
||||||
|
chatId, messageId, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
await getPromiseActions().loadMessage({ chatId, messageId });
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
|
||||||
|
if (!message || !message.content.action) return;
|
||||||
|
|
||||||
|
const action = message.content.action;
|
||||||
|
if (action.type !== 'starGift' && action.type !== 'starGiftUnique') return;
|
||||||
|
|
||||||
|
const starGift = action.type === 'starGift' ? action : undefined;
|
||||||
|
const uniqueGift = action.type === 'starGiftUnique' ? action : undefined;
|
||||||
|
const giftMsgId = starGift?.giftMsgId;
|
||||||
|
|
||||||
|
const giftReceiverId = action.peerId || (message.isOutgoing ? message.chatId : global.currentUserId!);
|
||||||
|
|
||||||
|
const inputGift: ApiInputSavedStarGift = (() => {
|
||||||
|
if (giftMsgId) {
|
||||||
|
return { type: 'user', messageId: giftMsgId };
|
||||||
|
}
|
||||||
|
if (action.savedId) {
|
||||||
|
return { type: 'chat', chatId, savedId: action.savedId };
|
||||||
|
}
|
||||||
|
return { type: 'user', messageId };
|
||||||
|
})();
|
||||||
|
|
||||||
|
const fromId = action.fromId || (message.isOutgoing ? global.currentUserId! : message.chatId);
|
||||||
|
|
||||||
|
const gift: ApiSavedStarGift = {
|
||||||
|
date: message.date,
|
||||||
|
gift: action.gift,
|
||||||
|
message: starGift?.message,
|
||||||
|
starsToConvert: starGift?.starsToConvert,
|
||||||
|
isNameHidden: starGift?.isNameHidden,
|
||||||
|
isUnsaved: !action.isSaved,
|
||||||
|
fromId,
|
||||||
|
messageId: message.id,
|
||||||
|
isConverted: starGift?.isConverted,
|
||||||
|
upgradeMsgId: starGift?.upgradeMsgId,
|
||||||
|
canUpgrade: starGift?.canUpgrade,
|
||||||
|
alreadyPaidUpgradeStars: starGift?.alreadyPaidUpgradeStars,
|
||||||
|
inputGift,
|
||||||
|
canExportAt: uniqueGift?.canExportAt,
|
||||||
|
savedId: action.savedId,
|
||||||
|
transferStars: uniqueGift?.transferStars,
|
||||||
|
dropOriginalDetailsStars: uniqueGift?.dropOriginalDetailsStars,
|
||||||
|
prepaidUpgradeHash: starGift?.prepaidUpgradeHash,
|
||||||
|
canCraftAt: uniqueGift?.canCraftAt,
|
||||||
|
};
|
||||||
|
|
||||||
|
actions.openGiftInfoModal({ peerId: giftReceiverId, gift, tabId });
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('openGiftInfoValueModal', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { gift, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
const result = await callApi('fetchUniqueStarGiftValueInfo', { slug: gift.slug });
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftInfoValueModal: {
|
||||||
|
valueInfo: result,
|
||||||
|
gift,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('openGiftCraftModal', async (global, _actions, payload): Promise<void> => {
|
||||||
|
const { gift, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
const uniqueGift = gift?.gift.type === 'starGiftUnique' ? gift.gift : undefined;
|
||||||
|
const regularGiftId = uniqueGift?.regularGiftId;
|
||||||
|
|
||||||
|
let previewAttributes: ApiStarGiftAttribute[] | undefined;
|
||||||
|
|
||||||
|
if (regularGiftId) {
|
||||||
|
const result = await callApi('fetchStarGiftUpgradeAttributes', { giftId: regularGiftId });
|
||||||
|
if (result) {
|
||||||
|
const craftableModels = result.attributes.filter(
|
||||||
|
(attr) => attr.type === 'model' && attr.rarity.type !== 'regular',
|
||||||
|
);
|
||||||
|
preloadGiftAttributeStickers(craftableModels);
|
||||||
|
previewAttributes = craftableModels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
regularGiftId,
|
||||||
|
regularGiftTitle: uniqueGift?.title,
|
||||||
|
gift1: gift,
|
||||||
|
marketFilter: { sortType: 'byPrice' },
|
||||||
|
marketUpdateIteration: 0,
|
||||||
|
previewAttributes,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('openGiftCraftSelectModal', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { slotIndex, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
const tabState = selectTabState(global, tabId);
|
||||||
|
const craftModal = tabState.giftCraftModal;
|
||||||
|
if (!craftModal?.regularGiftId) return;
|
||||||
|
|
||||||
|
const shouldLoadMyGifts = !craftModal.myCraftableGifts || craftModal.shouldRefreshMyCraftableGifts;
|
||||||
|
const shouldLoadMarketGifts = !craftModal.marketCraftableGifts;
|
||||||
|
|
||||||
|
if (!shouldLoadMyGifts && !shouldLoadMarketGifts) {
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftSelectModal: { slotIndex },
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftSelectModal: { slotIndex, isLoading: true },
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
|
||||||
|
const [myGiftsResult, marketGiftsResult] = await Promise.all([
|
||||||
|
shouldLoadMyGifts
|
||||||
|
? callApi('fetchCraftStarGifts', { giftId: craftModal.regularGiftId, peerId: global.currentUserId! })
|
||||||
|
: undefined,
|
||||||
|
shouldLoadMarketGifts
|
||||||
|
? callApi('fetchResaleGifts', {
|
||||||
|
giftId: craftModal.regularGiftId,
|
||||||
|
filter: craftModal.marketFilter,
|
||||||
|
forCraft: true,
|
||||||
|
})
|
||||||
|
: undefined,
|
||||||
|
]);
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
const currentCraftModal = selectTabState(global, tabId).giftCraftModal;
|
||||||
|
const currentSelectModal = selectTabState(global, tabId).giftCraftSelectModal;
|
||||||
|
if (!currentCraftModal || !currentSelectModal) return;
|
||||||
|
|
||||||
|
// Filter to only unique gifts
|
||||||
|
const savedGifts = myGiftsResult?.gifts.filter((g) => g.gift.type === 'starGiftUnique');
|
||||||
|
const marketGifts = marketGiftsResult?.gifts.filter(
|
||||||
|
(g): g is ApiStarGiftUnique => g.type === 'starGiftUnique',
|
||||||
|
);
|
||||||
|
|
||||||
|
const didLoadMyGifts = shouldLoadMyGifts && myGiftsResult;
|
||||||
|
const didLoadMarketGifts = shouldLoadMarketGifts && marketGiftsResult;
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...currentCraftModal,
|
||||||
|
myCraftableGifts: didLoadMyGifts ? savedGifts : currentCraftModal.myCraftableGifts,
|
||||||
|
myCraftableGiftsNextOffset: didLoadMyGifts
|
||||||
|
? myGiftsResult.nextOffset : currentCraftModal.myCraftableGiftsNextOffset,
|
||||||
|
shouldRefreshMyCraftableGifts: shouldLoadMyGifts ? !myGiftsResult :
|
||||||
|
currentCraftModal.shouldRefreshMyCraftableGifts,
|
||||||
|
marketCraftableGifts: didLoadMarketGifts ? marketGifts : currentCraftModal.marketCraftableGifts,
|
||||||
|
marketCraftableGiftsNextOffset: didLoadMarketGifts
|
||||||
|
? marketGiftsResult.nextOffset : currentCraftModal.marketCraftableGiftsNextOffset,
|
||||||
|
marketCraftableGiftsCount: didLoadMarketGifts
|
||||||
|
? marketGiftsResult.count : currentCraftModal.marketCraftableGiftsCount,
|
||||||
|
marketAttributes: didLoadMarketGifts ? marketGiftsResult.attributes : currentCraftModal.marketAttributes,
|
||||||
|
marketCounters: didLoadMarketGifts ? marketGiftsResult.counters : currentCraftModal.marketCounters,
|
||||||
|
marketAttributesHash: didLoadMarketGifts
|
||||||
|
? marketGiftsResult.attributesHash : currentCraftModal.marketAttributesHash,
|
||||||
|
},
|
||||||
|
giftCraftSelectModal: {
|
||||||
|
...currentSelectModal,
|
||||||
|
isLoading: false,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('loadMoreCraftableGifts', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
const tabState = selectTabState(global, tabId);
|
||||||
|
const craftModal = tabState.giftCraftModal;
|
||||||
|
if (!craftModal?.myCraftableGiftsNextOffset) return;
|
||||||
|
|
||||||
|
const gift1Unique = craftModal.gift1?.gift.type === 'starGiftUnique' ? craftModal.gift1.gift : undefined;
|
||||||
|
if (!gift1Unique?.regularGiftId) return;
|
||||||
|
|
||||||
|
const result = await callApi('fetchCraftStarGifts', {
|
||||||
|
giftId: gift1Unique.regularGiftId,
|
||||||
|
peerId: global.currentUserId!,
|
||||||
|
offset: craftModal.myCraftableGiftsNextOffset,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
const currentCraftModal = selectTabState(global, tabId).giftCraftModal;
|
||||||
|
if (!currentCraftModal) return;
|
||||||
|
|
||||||
|
// Filter to only unique gifts
|
||||||
|
const newSavedGifts = result.gifts.filter((g) => g.gift.type === 'starGiftUnique');
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...currentCraftModal,
|
||||||
|
myCraftableGifts: [...(currentCraftModal.myCraftableGifts || []), ...newSavedGifts],
|
||||||
|
myCraftableGiftsNextOffset: result.nextOffset,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('loadMoreMarketCraftableGifts', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
const tabState = selectTabState(global, tabId);
|
||||||
|
const craftModal = tabState.giftCraftModal;
|
||||||
|
if (!craftModal?.regularGiftId) return;
|
||||||
|
|
||||||
|
if (craftModal.isMarketLoading) return;
|
||||||
|
if (craftModal.marketCraftableGifts && !craftModal.marketCraftableGiftsNextOffset) return;
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...craftModal,
|
||||||
|
isMarketLoading: true,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
|
||||||
|
const result = await callApi('fetchResaleGifts', {
|
||||||
|
giftId: craftModal.regularGiftId,
|
||||||
|
offset: craftModal.marketCraftableGiftsNextOffset,
|
||||||
|
filter: craftModal.marketFilter,
|
||||||
|
forCraft: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
const currentCraftModal = selectTabState(global, tabId).giftCraftModal;
|
||||||
|
if (!currentCraftModal) return;
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: { ...currentCraftModal, isMarketLoading: false },
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newGifts = result.gifts.filter((g): g is ApiStarGiftUnique => g.type === 'starGiftUnique');
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...currentCraftModal,
|
||||||
|
marketCraftableGifts: [...(currentCraftModal.marketCraftableGifts || []), ...newGifts],
|
||||||
|
marketCraftableGiftsNextOffset: result.nextOffset,
|
||||||
|
isMarketLoading: false,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('updateCraftGiftsFilter', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { filter, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
const tabState = selectTabState(global, tabId);
|
||||||
|
const modal = tabState.giftCraftModal;
|
||||||
|
if (!modal?.regularGiftId) return;
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...modal,
|
||||||
|
marketFilter: filter,
|
||||||
|
isMarketLoading: true,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
|
||||||
|
const result = await callApi('fetchResaleGifts', {
|
||||||
|
giftId: modal.regularGiftId,
|
||||||
|
filter,
|
||||||
|
forCraft: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
const currentModal = selectTabState(global, tabId).giftCraftModal;
|
||||||
|
if (!currentModal) return;
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: { ...currentModal, isMarketLoading: false },
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newGifts = result.gifts.filter((g): g is ApiStarGiftUnique => g.type === 'starGiftUnique');
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...currentModal,
|
||||||
|
marketCraftableGifts: newGifts,
|
||||||
|
marketCraftableGiftsNextOffset: result.nextOffset,
|
||||||
|
marketCraftableGiftsCount: result.count,
|
||||||
|
marketCounters: result.counters,
|
||||||
|
marketUpdateIteration: currentModal.marketUpdateIteration + 1,
|
||||||
|
isMarketLoading: false,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('craftStarGift', async (global, _actions, payload): Promise<void> => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
const tabState = selectTabState(global, tabId);
|
||||||
|
const modal = tabState.giftCraftModal;
|
||||||
|
|
||||||
|
if (!modal?.regularGiftId) return;
|
||||||
|
|
||||||
|
const savedGifts = [modal.gift1, modal.gift2, modal.gift3, modal.gift4].filter(
|
||||||
|
(g): g is ApiSavedStarGift => Boolean(g),
|
||||||
|
);
|
||||||
|
if (savedGifts.length === 0) return;
|
||||||
|
|
||||||
|
const inputSavedGifts = savedGifts
|
||||||
|
.map((g) => g.inputGift && getRequestInputSavedStarGift(global, g.inputGift))
|
||||||
|
.filter((g): g is ApiRequestInputSavedStarGift => Boolean(g));
|
||||||
|
|
||||||
|
if (inputSavedGifts.length === 0) return;
|
||||||
|
|
||||||
|
const result = await callApi('craftStarGift', { inputSavedGifts });
|
||||||
|
|
||||||
|
if (result?.error) {
|
||||||
|
global = getGlobal();
|
||||||
|
const currentModal = selectTabState(global, tabId).giftCraftModal;
|
||||||
|
if (!currentModal) return;
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...currentModal,
|
||||||
|
craftResult: { success: false, isError: true },
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('openAboutStarGiftModal', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
const result = await callApi('fetchPremiumPromo');
|
||||||
|
|
||||||
|
let videoId: string | undefined;
|
||||||
|
let videoThumbnail;
|
||||||
|
|
||||||
|
if (result?.promo) {
|
||||||
|
const giftsIndex = result.promo.videoSections.indexOf('gifts');
|
||||||
|
if (giftsIndex !== -1 && giftsIndex < result.promo.videos.length) {
|
||||||
|
const video = result.promo.videos[giftsIndex];
|
||||||
|
videoId = video.id;
|
||||||
|
videoThumbnail = video.thumbnail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = updateTabState(global, {
|
||||||
|
aboutStarGiftModal: { videoId, videoThumbnail },
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('openGiftPreviewModal', async (global, _actions, payload): Promise<void> => {
|
addActionHandler('openGiftPreviewModal', async (global, _actions, payload): Promise<void> => {
|
||||||
const { originGift, tabId = getCurrentTabId() } = payload;
|
const { originGift, shouldShowCraftableOnStart, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
const giftId = originGift.type === 'starGiftUnique' ? originGift.regularGiftId : originGift.id;
|
const giftId = originGift.type === 'starGiftUnique' ? originGift.regularGiftId : originGift.id;
|
||||||
const result = await callApi('fetchStarGiftUpgradeAttributes', { giftId });
|
const result = await callApi('fetchStarGiftUpgradeAttributes', { giftId });
|
||||||
@ -765,6 +1155,7 @@ addActionHandler('openGiftPreviewModal', async (global, _actions, payload): Prom
|
|||||||
giftPreviewModal: {
|
giftPreviewModal: {
|
||||||
originGift,
|
originGift,
|
||||||
attributes: result.attributes,
|
attributes: result.attributes,
|
||||||
|
shouldShowCraftableOnStart,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|||||||
@ -339,6 +339,15 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
|
|
||||||
actions.reloadPeerSavedGifts({ peerId: global.currentUserId! });
|
actions.reloadPeerSavedGifts({ peerId: global.currentUserId! });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tabState.giftCraftModal && actionStarGift.isCrafted) {
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...tabState.giftCraftModal,
|
||||||
|
craftResult: { success: true, gift: actionStarGift },
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|||||||
@ -162,6 +162,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
const starGiftModalState = selectTabState(global, tabId).giftInfoModal;
|
const starGiftModalState = selectTabState(global, tabId).giftInfoModal;
|
||||||
|
|
||||||
if (starGiftModalState) {
|
if (starGiftModalState) {
|
||||||
|
const { craftSlotIndex, gift } = starGiftModalState;
|
||||||
|
const actualGift = 'gift' in gift ? gift.gift : gift;
|
||||||
|
const giftId = actualGift.type === 'starGiftUnique' ? actualGift.id : undefined;
|
||||||
|
|
||||||
actions.showNotification({
|
actions.showNotification({
|
||||||
message: {
|
message: {
|
||||||
key: 'StarsGiftBought',
|
key: 'StarsGiftBought',
|
||||||
@ -173,6 +177,11 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
}
|
}
|
||||||
actions.reloadPeerSavedGifts({ peerId: inputInvoice.peerId });
|
actions.reloadPeerSavedGifts({ peerId: inputInvoice.peerId });
|
||||||
actions.requestConfetti({ withStars: true, tabId });
|
actions.requestConfetti({ withStars: true, tabId });
|
||||||
|
|
||||||
|
if (craftSlotIndex !== undefined && giftId) {
|
||||||
|
actions.selectPurchasedGiftForCraft({ giftId, slotIndex: craftSlotIndex, tabId });
|
||||||
|
}
|
||||||
|
|
||||||
actions.closeGiftInfoModal({ tabId });
|
actions.closeGiftInfoModal({ tabId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,5 +279,21 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'updateStarGiftCraftFail': {
|
||||||
|
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
||||||
|
const modal = selectTabState(global, tabId).giftCraftModal;
|
||||||
|
if (modal) {
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...modal,
|
||||||
|
craftResult: { success: false },
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setGlobal(global);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import { getPromiseActions } from '../../../global';
|
import type { ApiSavedStarGift, ApiStarGiftUnique } from '../../../api/types';
|
||||||
|
|
||||||
import type { ApiInputSavedStarGift, ApiSavedStarGift } from '../../../api/types';
|
|
||||||
import type { ActionReturnType } from '../../types';
|
import type { ActionReturnType } from '../../types';
|
||||||
|
|
||||||
import { STARS_CURRENCY_CODE } from '../../../config';
|
import { STARS_CURRENCY_CODE } from '../../../config';
|
||||||
import { selectChat } from '../../../global/selectors';
|
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
import * as langProvider from '../../../util/oldLangProvider';
|
import * as langProvider from '../../../util/oldLangProvider';
|
||||||
import { callApi } from '../../../api/gramjs';
|
|
||||||
import { addTabStateResetterAction } from '../../helpers/meta';
|
import { addTabStateResetterAction } from '../../helpers/meta';
|
||||||
import { getPrizeStarsTransactionFromGiveaway, getStarsTransactionFromGift } from '../../helpers/payments';
|
import { getPrizeStarsTransactionFromGiveaway, getStarsTransactionFromGift } from '../../helpers/payments';
|
||||||
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
import { addActionHandler, setGlobal } from '../../index';
|
||||||
import { clearStarPayment, openStarsTransactionModal } from '../../reducers';
|
import { clearStarPayment, openStarsTransactionModal } from '../../reducers';
|
||||||
import { removeGiftAuction } from '../../reducers/gifts';
|
import { removeGiftAuction } from '../../reducers/gifts';
|
||||||
import { updateTabState } from '../../reducers/tabs';
|
import { updateTabState } from '../../reducers/tabs';
|
||||||
@ -18,6 +14,14 @@ import {
|
|||||||
selectChatMessage, selectIsCurrentUserFrozen, selectShouldRemoveGiftAuction, selectStarsPayment, selectTabState,
|
selectChatMessage, selectIsCurrentUserFrozen, selectShouldRemoveGiftAuction, selectStarsPayment, selectTabState,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
|
|
||||||
|
function buildShortSavedGift(gift: ApiStarGiftUnique, fromId?: string): ApiSavedStarGift {
|
||||||
|
return {
|
||||||
|
gift,
|
||||||
|
date: Math.floor(Date.now() / 1000),
|
||||||
|
fromId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
addActionHandler('processOriginStarsPayment', (global, actions, payload): ActionReturnType => {
|
addActionHandler('processOriginStarsPayment', (global, actions, payload): ActionReturnType => {
|
||||||
const { originData, status, tabId = getCurrentTabId() } = payload;
|
const { originData, status, tabId = getCurrentTabId() } = payload;
|
||||||
const {
|
const {
|
||||||
@ -248,69 +252,9 @@ addActionHandler('closeStarsGiftModal', (global, actions, payload): ActionReturn
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openGiftInfoModalFromMessage', async (global, actions, payload): Promise<void> => {
|
|
||||||
const {
|
|
||||||
chatId, messageId, tabId = getCurrentTabId(),
|
|
||||||
} = payload;
|
|
||||||
|
|
||||||
const chat = selectChat(global, chatId);
|
|
||||||
if (!chat) return;
|
|
||||||
|
|
||||||
await getPromiseActions().loadMessage({ chatId, messageId });
|
|
||||||
|
|
||||||
global = getGlobal();
|
|
||||||
const message = selectChatMessage(global, chatId, messageId);
|
|
||||||
|
|
||||||
if (!message || !message.content.action) return;
|
|
||||||
|
|
||||||
const action = message.content.action;
|
|
||||||
if (action.type !== 'starGift' && action.type !== 'starGiftUnique') return;
|
|
||||||
|
|
||||||
const starGift = action.type === 'starGift' ? action : undefined;
|
|
||||||
const uniqueGift = action.type === 'starGiftUnique' ? action : undefined;
|
|
||||||
const giftMsgId = starGift?.giftMsgId;
|
|
||||||
|
|
||||||
const giftReceiverId = action.peerId || (message.isOutgoing ? message.chatId : global.currentUserId!);
|
|
||||||
|
|
||||||
const inputGift: ApiInputSavedStarGift = (() => {
|
|
||||||
if (giftMsgId) {
|
|
||||||
return { type: 'user', messageId: giftMsgId };
|
|
||||||
}
|
|
||||||
if (action.savedId) {
|
|
||||||
return { type: 'chat', chatId, savedId: action.savedId };
|
|
||||||
}
|
|
||||||
return { type: 'user', messageId };
|
|
||||||
})();
|
|
||||||
|
|
||||||
const fromId = action.fromId || (message.isOutgoing ? global.currentUserId! : message.chatId);
|
|
||||||
|
|
||||||
const gift: ApiSavedStarGift = {
|
|
||||||
date: message.date,
|
|
||||||
gift: action.gift,
|
|
||||||
message: starGift?.message,
|
|
||||||
starsToConvert: starGift?.starsToConvert,
|
|
||||||
isNameHidden: starGift?.isNameHidden,
|
|
||||||
isUnsaved: !action.isSaved,
|
|
||||||
fromId,
|
|
||||||
messageId: message.id,
|
|
||||||
isConverted: starGift?.isConverted,
|
|
||||||
upgradeMsgId: starGift?.upgradeMsgId,
|
|
||||||
canUpgrade: starGift?.canUpgrade,
|
|
||||||
alreadyPaidUpgradeStars: starGift?.alreadyPaidUpgradeStars,
|
|
||||||
inputGift,
|
|
||||||
canExportAt: uniqueGift?.canExportAt,
|
|
||||||
savedId: action.savedId,
|
|
||||||
transferStars: uniqueGift?.transferStars,
|
|
||||||
dropOriginalDetailsStars: uniqueGift?.dropOriginalDetailsStars,
|
|
||||||
prepaidUpgradeHash: starGift?.prepaidUpgradeHash,
|
|
||||||
};
|
|
||||||
|
|
||||||
actions.openGiftInfoModal({ peerId: giftReceiverId, gift, tabId });
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('openGiftInfoModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openGiftInfoModal', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
gift, tabId = getCurrentTabId(),
|
gift, craftSlotIndex, tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
const peerId = 'peerId' in payload ? payload.peerId : undefined;
|
const peerId = 'peerId' in payload ? payload.peerId : undefined;
|
||||||
@ -321,6 +265,7 @@ addActionHandler('openGiftInfoModal', (global, actions, payload): ActionReturnTy
|
|||||||
peerId,
|
peerId,
|
||||||
gift,
|
gift,
|
||||||
recipientId,
|
recipientId,
|
||||||
|
craftSlotIndex,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
@ -398,22 +343,6 @@ addActionHandler('closeResaleGiftsMarket', (global, actions, payload): ActionRet
|
|||||||
return global;
|
return global;
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openGiftInfoValueModal', async (global, actions, payload): Promise<void> => {
|
|
||||||
const { gift, tabId = getCurrentTabId() } = payload;
|
|
||||||
|
|
||||||
const result = await callApi('fetchUniqueStarGiftValueInfo', { slug: gift.slug });
|
|
||||||
if (!result) return;
|
|
||||||
|
|
||||||
global = getGlobal();
|
|
||||||
global = updateTabState(global, {
|
|
||||||
giftInfoValueModal: {
|
|
||||||
valueInfo: result,
|
|
||||||
gift,
|
|
||||||
},
|
|
||||||
}, tabId);
|
|
||||||
setGlobal(global);
|
|
||||||
});
|
|
||||||
|
|
||||||
addTabStateResetterAction('closeGiftInfoModal', 'giftInfoModal');
|
addTabStateResetterAction('closeGiftInfoModal', 'giftInfoModal');
|
||||||
|
|
||||||
addTabStateResetterAction('closeGiftInfoValueModal', 'giftInfoValueModal');
|
addTabStateResetterAction('closeGiftInfoValueModal', 'giftInfoValueModal');
|
||||||
@ -422,6 +351,95 @@ addTabStateResetterAction('closeGiftResalePriceComposerModal', 'giftResalePriceC
|
|||||||
|
|
||||||
addTabStateResetterAction('closeGiftUpgradeModal', 'giftUpgradeModal');
|
addTabStateResetterAction('closeGiftUpgradeModal', 'giftUpgradeModal');
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeGiftCraftModal', 'giftCraftModal');
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeGiftCraftSelectModal', 'giftCraftSelectModal');
|
||||||
|
|
||||||
|
addActionHandler('openGiftCraftInfoModal', (global, _actions, payload): ActionReturnType => {
|
||||||
|
const { gift, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
giftCraftInfoModal: { gift },
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeGiftCraftInfoModal', 'giftCraftInfoModal');
|
||||||
|
|
||||||
|
addActionHandler('selectGiftForCraft', (global, _actions, payload): ActionReturnType => {
|
||||||
|
const { gift, slotIndex, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
const tabState = selectTabState(global, tabId);
|
||||||
|
const modal = tabState.giftCraftModal;
|
||||||
|
|
||||||
|
if (!modal) return undefined;
|
||||||
|
|
||||||
|
const slots = [modal.gift1, modal.gift2, modal.gift3, modal.gift4];
|
||||||
|
slots[slotIndex] = gift;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...modal,
|
||||||
|
gift1: slots[0],
|
||||||
|
gift2: slots[1],
|
||||||
|
gift3: slots[2],
|
||||||
|
gift4: slots[3],
|
||||||
|
},
|
||||||
|
giftCraftSelectModal: gift ? undefined : tabState.giftCraftSelectModal,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('selectPurchasedGiftForCraft', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { giftId, slotIndex, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
const tabState = selectTabState(global, tabId);
|
||||||
|
const craftModal = tabState.giftCraftModal;
|
||||||
|
const giftInfoModal = tabState.giftInfoModal;
|
||||||
|
if (!craftModal) return undefined;
|
||||||
|
|
||||||
|
const giftFromModal = giftInfoModal?.gift;
|
||||||
|
const actualGift = giftFromModal && 'gift' in giftFromModal ? giftFromModal.gift : giftFromModal;
|
||||||
|
|
||||||
|
if (!actualGift || actualGift.type !== 'starGiftUnique' || actualGift.id !== giftId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shortSavedGift = buildShortSavedGift(actualGift, global.currentUserId);
|
||||||
|
|
||||||
|
const slots = [craftModal.gift1, craftModal.gift2, craftModal.gift3, craftModal.gift4];
|
||||||
|
slots[slotIndex] = shortSavedGift;
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...craftModal,
|
||||||
|
gift1: slots[0],
|
||||||
|
gift2: slots[1],
|
||||||
|
gift3: slots[2],
|
||||||
|
gift4: slots[3],
|
||||||
|
shouldRefreshMyCraftableGifts: true,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
|
||||||
|
actions.closeGiftCraftSelectModal({ tabId });
|
||||||
|
|
||||||
|
return global;
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('resetGiftCraftResult', (global, _actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
const modal = selectTabState(global, tabId).giftCraftModal;
|
||||||
|
if (!modal) return undefined;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
giftCraftModal: {
|
||||||
|
...modal,
|
||||||
|
craftResult: undefined,
|
||||||
|
gift1: undefined,
|
||||||
|
gift2: undefined,
|
||||||
|
gift3: undefined,
|
||||||
|
gift4: undefined,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
addTabStateResetterAction('closeGiftPreviewModal', 'giftPreviewModal');
|
addTabStateResetterAction('closeGiftPreviewModal', 'giftPreviewModal');
|
||||||
|
|
||||||
addActionHandler('closeGiftAuctionModal', (global, _actions, payload): ActionReturnType => {
|
addActionHandler('closeGiftAuctionModal', (global, _actions, payload): ActionReturnType => {
|
||||||
@ -476,30 +494,6 @@ addActionHandler('closeGiftAuctionInfoModal', (global, _actions, payload): Actio
|
|||||||
return global;
|
return global;
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openAboutStarGiftModal', async (global, _actions, payload): Promise<void> => {
|
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
const result = await callApi('fetchPremiumPromo');
|
|
||||||
|
|
||||||
let videoId: string | undefined;
|
|
||||||
let videoThumbnail;
|
|
||||||
|
|
||||||
if (result?.promo) {
|
|
||||||
const giftsIndex = result.promo.videoSections.indexOf('gifts');
|
|
||||||
if (giftsIndex !== -1 && giftsIndex < result.promo.videos.length) {
|
|
||||||
const video = result.promo.videos[giftsIndex];
|
|
||||||
videoId = video.id;
|
|
||||||
videoThumbnail = video.thumbnail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
global = getGlobal();
|
|
||||||
global = updateTabState(global, {
|
|
||||||
aboutStarGiftModal: { videoId, videoThumbnail },
|
|
||||||
}, tabId);
|
|
||||||
setGlobal(global);
|
|
||||||
});
|
|
||||||
|
|
||||||
addTabStateResetterAction('closeAboutStarGiftModal', 'aboutStarGiftModal');
|
addTabStateResetterAction('closeAboutStarGiftModal', 'aboutStarGiftModal');
|
||||||
|
|
||||||
addActionHandler('openGiftAuctionChangeRecipientModal', (global, _actions, payload): ActionReturnType => {
|
addActionHandler('openGiftAuctionChangeRecipientModal', (global, _actions, payload): ActionReturnType => {
|
||||||
|
|||||||
@ -2640,6 +2640,9 @@ export interface ActionPayloads {
|
|||||||
updateResaleGiftsFilter: {
|
updateResaleGiftsFilter: {
|
||||||
filter: ResaleGiftsFilterOptions;
|
filter: ResaleGiftsFilterOptions;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
updateCraftGiftsFilter: {
|
||||||
|
filter: ResaleGiftsFilterOptions;
|
||||||
|
} & WithTabId;
|
||||||
loadResaleGifts: {
|
loadResaleGifts: {
|
||||||
giftId: string;
|
giftId: string;
|
||||||
shouldRefresh?: boolean;
|
shouldRefresh?: boolean;
|
||||||
@ -2678,8 +2681,10 @@ export interface ActionPayloads {
|
|||||||
peerId: string;
|
peerId: string;
|
||||||
recipientId?: string;
|
recipientId?: string;
|
||||||
gift: ApiSavedStarGift;
|
gift: ApiSavedStarGift;
|
||||||
|
craftSlotIndex?: number;
|
||||||
} | {
|
} | {
|
||||||
gift: ApiStarGift;
|
gift: ApiStarGift;
|
||||||
|
craftSlotIndex?: number;
|
||||||
}) & WithTabId;
|
}) & WithTabId;
|
||||||
openLockedGiftModalInfo: {
|
openLockedGiftModalInfo: {
|
||||||
untilDate?: number;
|
untilDate?: number;
|
||||||
@ -2709,6 +2714,31 @@ export interface ActionPayloads {
|
|||||||
closeGiftUpgradeModal: WithTabId | undefined;
|
closeGiftUpgradeModal: WithTabId | undefined;
|
||||||
shiftGiftUpgradeNextPrice: WithTabId | undefined;
|
shiftGiftUpgradeNextPrice: WithTabId | undefined;
|
||||||
|
|
||||||
|
openGiftCraftModal: {
|
||||||
|
gift: ApiSavedStarGift;
|
||||||
|
} & WithTabId;
|
||||||
|
closeGiftCraftModal: WithTabId | undefined;
|
||||||
|
resetGiftCraftResult: WithTabId | undefined;
|
||||||
|
openGiftCraftSelectModal: {
|
||||||
|
slotIndex: number;
|
||||||
|
} & WithTabId;
|
||||||
|
closeGiftCraftSelectModal: WithTabId | undefined;
|
||||||
|
openGiftCraftInfoModal: {
|
||||||
|
gift: ApiStarGiftUnique;
|
||||||
|
} & WithTabId;
|
||||||
|
closeGiftCraftInfoModal: WithTabId | undefined;
|
||||||
|
selectGiftForCraft: {
|
||||||
|
gift?: ApiSavedStarGift;
|
||||||
|
slotIndex: number;
|
||||||
|
} & WithTabId;
|
||||||
|
selectPurchasedGiftForCraft: {
|
||||||
|
giftId: string;
|
||||||
|
slotIndex: number;
|
||||||
|
} & WithTabId;
|
||||||
|
loadMoreCraftableGifts: WithTabId | undefined;
|
||||||
|
loadMoreMarketCraftableGifts: WithTabId | undefined;
|
||||||
|
craftStarGift: WithTabId | undefined;
|
||||||
|
|
||||||
openStarGiftPriceDecreaseInfoModal: {
|
openStarGiftPriceDecreaseInfoModal: {
|
||||||
prices: ApiStarGiftUpgradePrice[];
|
prices: ApiStarGiftUpgradePrice[];
|
||||||
currentPrice: number;
|
currentPrice: number;
|
||||||
@ -2742,6 +2772,7 @@ export interface ActionPayloads {
|
|||||||
closeGiftInfoValueModal: WithTabId | undefined;
|
closeGiftInfoValueModal: WithTabId | undefined;
|
||||||
openGiftPreviewModal: {
|
openGiftPreviewModal: {
|
||||||
originGift: ApiStarGift;
|
originGift: ApiStarGift;
|
||||||
|
shouldShowCraftableOnStart?: boolean;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeGiftPreviewModal: WithTabId | undefined;
|
closeGiftPreviewModal: WithTabId | undefined;
|
||||||
loadActiveGiftAuctions: undefined;
|
loadActiveGiftAuctions: undefined;
|
||||||
|
|||||||
@ -841,6 +841,7 @@ export type TabState = {
|
|||||||
peerId?: string;
|
peerId?: string;
|
||||||
recipientId?: string;
|
recipientId?: string;
|
||||||
gift: ApiSavedStarGift | ApiStarGift;
|
gift: ApiSavedStarGift | ApiStarGift;
|
||||||
|
craftSlotIndex?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
giftInfoValueModal?: {
|
giftInfoValueModal?: {
|
||||||
@ -891,6 +892,44 @@ export type TabState = {
|
|||||||
maxPrice?: number;
|
maxPrice?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
giftCraftModal?: {
|
||||||
|
regularGiftId?: string;
|
||||||
|
regularGiftTitle?: string;
|
||||||
|
gift1?: ApiSavedStarGift;
|
||||||
|
gift2?: ApiSavedStarGift;
|
||||||
|
gift3?: ApiSavedStarGift;
|
||||||
|
gift4?: ApiSavedStarGift;
|
||||||
|
previewAttributes?: ApiStarGiftAttribute[];
|
||||||
|
myCraftableGifts?: ApiSavedStarGift[];
|
||||||
|
myCraftableGiftsNextOffset?: string;
|
||||||
|
shouldRefreshMyCraftableGifts?: boolean;
|
||||||
|
marketCraftableGifts?: ApiStarGiftUnique[];
|
||||||
|
marketCraftableGiftsNextOffset?: string;
|
||||||
|
marketCraftableGiftsCount?: number;
|
||||||
|
isMarketLoading?: boolean;
|
||||||
|
marketFilter: ResaleGiftsFilterOptions;
|
||||||
|
marketAttributes?: ApiStarGiftAttribute[];
|
||||||
|
marketCounters?: ApiStarGiftAttributeCounter[];
|
||||||
|
marketAttributesHash?: string;
|
||||||
|
marketUpdateIteration: number;
|
||||||
|
craftResult?: {
|
||||||
|
success: true;
|
||||||
|
gift: ApiStarGiftUnique;
|
||||||
|
} | {
|
||||||
|
success: false;
|
||||||
|
isError?: true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
giftCraftSelectModal?: {
|
||||||
|
slotIndex: number;
|
||||||
|
isLoading?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
giftCraftInfoModal?: {
|
||||||
|
gift: ApiStarGiftUnique;
|
||||||
|
};
|
||||||
|
|
||||||
giftWithdrawModal?: {
|
giftWithdrawModal?: {
|
||||||
gift: ApiSavedStarGift;
|
gift: ApiSavedStarGift;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
@ -904,6 +943,7 @@ export type TabState = {
|
|||||||
giftPreviewModal?: {
|
giftPreviewModal?: {
|
||||||
attributes: ApiStarGiftAttribute[];
|
attributes: ApiStarGiftAttribute[];
|
||||||
originGift: ApiStarGift;
|
originGift: ApiStarGift;
|
||||||
|
shouldShowCraftableOnStart?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
giftAuctionModal?: {
|
giftAuctionModal?: {
|
||||||
|
|||||||
@ -1905,6 +1905,8 @@ payments.getStarGiftAuctionAcquiredGifts#6ba2cbec gift_id:long = payments.StarGi
|
|||||||
payments.getStarGiftActiveAuctions#a5d0514d hash:long = payments.StarGiftActiveAuctions;
|
payments.getStarGiftActiveAuctions#a5d0514d hash:long = payments.StarGiftActiveAuctions;
|
||||||
payments.resolveStarGiftOffer#e9ce781c flags:# decline:flags.0?true offer_msg_id:int = Updates;
|
payments.resolveStarGiftOffer#e9ce781c flags:# decline:flags.0?true offer_msg_id:int = Updates;
|
||||||
payments.getStarGiftUpgradeAttributes#6d038b58 gift_id:long = payments.StarGiftUpgradeAttributes;
|
payments.getStarGiftUpgradeAttributes#6d038b58 gift_id:long = payments.StarGiftUpgradeAttributes;
|
||||||
|
payments.getCraftStarGifts#fd05dd00 gift_id:long offset:string limit:int = payments.SavedStarGifts;
|
||||||
|
payments.craftStarGift#b0f9684f stargift:Vector<InputSavedStarGift> = Updates;
|
||||||
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||||
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||||
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;
|
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||||
|
|||||||
@ -359,6 +359,8 @@
|
|||||||
"payments.getStarGiftAuctionAcquiredGifts",
|
"payments.getStarGiftAuctionAcquiredGifts",
|
||||||
"payments.getStarGiftActiveAuctions",
|
"payments.getStarGiftActiveAuctions",
|
||||||
"payments.resolveStarGiftOffer",
|
"payments.resolveStarGiftOffer",
|
||||||
|
"payments.getCraftStarGifts",
|
||||||
|
"payments.craftStarGift",
|
||||||
"payments.getStarGiftUpgradeAttributes",
|
"payments.getStarGiftUpgradeAttributes",
|
||||||
"langpack.getLangPack",
|
"langpack.getLangPack",
|
||||||
"langpack.getStrings",
|
"langpack.getStrings",
|
||||||
|
|||||||
@ -16,315 +16,319 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$icons-map: (
|
$icons-map: (
|
||||||
"active-sessions": "\f101",
|
"zoom-out": "\f101",
|
||||||
"add-one-badge": "\f102",
|
"zoom-in": "\f102",
|
||||||
"add-user-filled": "\f103",
|
"word-wrap": "\f103",
|
||||||
"add-user": "\f104",
|
"webapp": "\f104",
|
||||||
"add": "\f105",
|
"web": "\f105",
|
||||||
"admin": "\f106",
|
"warning": "\f106",
|
||||||
"allow-speak": "\f107",
|
"volume-3": "\f107",
|
||||||
"animals": "\f108",
|
"volume-2": "\f108",
|
||||||
"animations": "\f109",
|
"volume-1": "\f109",
|
||||||
"archive-filled": "\f10a",
|
"voice-chat": "\f10a",
|
||||||
"archive-from-main": "\f10b",
|
"view-once": "\f10b",
|
||||||
"archive-to-main": "\f10c",
|
"video": "\f10c",
|
||||||
"archive": "\f10d",
|
"video-stop": "\f10d",
|
||||||
"arrow-down-circle": "\f10e",
|
"video-outlined": "\f10e",
|
||||||
"arrow-down": "\f10f",
|
"user": "\f10f",
|
||||||
"arrow-left": "\f110",
|
"user-stars": "\f110",
|
||||||
"arrow-right": "\f111",
|
"user-online": "\f111",
|
||||||
"ask-support": "\f112",
|
"user-filled": "\f112",
|
||||||
"attach": "\f113",
|
"up": "\f113",
|
||||||
"auction-drop": "\f114",
|
"unread": "\f114",
|
||||||
"auction-filled": "\f115",
|
"unpin": "\f115",
|
||||||
"auction-next-round": "\f116",
|
"unmute": "\f116",
|
||||||
"auction": "\f117",
|
"unlock": "\f117",
|
||||||
"author-hidden": "\f118",
|
"unlock-badge": "\f118",
|
||||||
"avatar-archived-chats": "\f119",
|
"unlist": "\f119",
|
||||||
"avatar-deleted-account": "\f11a",
|
"unlist-outline": "\f11a",
|
||||||
"avatar-saved-messages": "\f11b",
|
"unique-profile": "\f11b",
|
||||||
"bold": "\f11c",
|
"understood": "\f11c",
|
||||||
"boost-outline": "\f11d",
|
"underlined": "\f11d",
|
||||||
"boost": "\f11e",
|
"unarchive": "\f11e",
|
||||||
"boostcircle": "\f11f",
|
"truck": "\f11f",
|
||||||
"boosts": "\f120",
|
"transcribe": "\f120",
|
||||||
"bot-command": "\f121",
|
"trade": "\f121",
|
||||||
"bot-commands-filled": "\f122",
|
"topic-new": "\f122",
|
||||||
"bots": "\f123",
|
"tools": "\f123",
|
||||||
"bug": "\f124",
|
"toncoin": "\f124",
|
||||||
"calendar-filter": "\f125",
|
"timer": "\f125",
|
||||||
"calendar": "\f126",
|
"tag": "\f126",
|
||||||
"camera-add": "\f127",
|
"tag-name": "\f127",
|
||||||
"camera": "\f128",
|
"tag-filter": "\f128",
|
||||||
"car": "\f129",
|
"tag-crossed": "\f129",
|
||||||
"card": "\f12a",
|
"tag-add": "\f12a",
|
||||||
"cash-circle": "\f12b",
|
"strikethrough": "\f12b",
|
||||||
"channel-filled": "\f12c",
|
"story-reply": "\f12c",
|
||||||
"channel": "\f12d",
|
"story-priority": "\f12d",
|
||||||
"channelviews": "\f12e",
|
"story-expired": "\f12e",
|
||||||
"chat-badge": "\f12f",
|
"story-caption": "\f12f",
|
||||||
"chats-badge": "\f130",
|
"stop": "\f130",
|
||||||
"check": "\f131",
|
"stop-raising-hand": "\f131",
|
||||||
"clock-edit": "\f132",
|
"stickers": "\f132",
|
||||||
"clock": "\f133",
|
"stealth-past": "\f133",
|
||||||
"close-circle": "\f134",
|
"stealth-future": "\f134",
|
||||||
"close-topic": "\f135",
|
"stats": "\f135",
|
||||||
"close": "\f136",
|
"stars-refund": "\f136",
|
||||||
"closed-gift": "\f137",
|
"stars-lock": "\f137",
|
||||||
"cloud-download": "\f138",
|
"star": "\f138",
|
||||||
"collapse-modal": "\f139",
|
"sport": "\f139",
|
||||||
"collapse": "\f13a",
|
"spoiler": "\f13a",
|
||||||
"colorize": "\f13b",
|
"spoiler-disable": "\f13b",
|
||||||
"comments-sticker": "\f13c",
|
"speaker": "\f13c",
|
||||||
"comments": "\f13d",
|
"speaker-story": "\f13d",
|
||||||
"copy-media": "\f13e",
|
"speaker-outline": "\f13e",
|
||||||
"copy": "\f13f",
|
"speaker-muted-story": "\f13f",
|
||||||
"crown-take-off-outline": "\f140",
|
"sort": "\f140",
|
||||||
"crown-take-off": "\f141",
|
"sort-by-price": "\f141",
|
||||||
"crown-wear-outline": "\f142",
|
"sort-by-number": "\f142",
|
||||||
"crown-wear": "\f143",
|
"sort-by-date": "\f143",
|
||||||
"darkmode": "\f144",
|
"smile": "\f144",
|
||||||
"data": "\f145",
|
"smallscreen": "\f145",
|
||||||
"delete-filled": "\f146",
|
"skip-previous": "\f146",
|
||||||
"delete-left": "\f147",
|
"skip-next": "\f147",
|
||||||
"delete-user": "\f148",
|
"sidebar": "\f148",
|
||||||
"delete": "\f149",
|
"show-message": "\f149",
|
||||||
"diamond": "\f14a",
|
"share-screen": "\f14a",
|
||||||
"document": "\f14b",
|
"share-screen-stop": "\f14b",
|
||||||
"double-badge": "\f14c",
|
"share-screen-outlined": "\f14c",
|
||||||
"down": "\f14d",
|
"share-filled": "\f14d",
|
||||||
"download": "\f14e",
|
"settings": "\f14e",
|
||||||
"dropdown-arrows": "\f14f",
|
"settings-filled": "\f14f",
|
||||||
"eats": "\f150",
|
"send": "\f150",
|
||||||
"edit": "\f151",
|
"send-outline": "\f151",
|
||||||
"email": "\f152",
|
"sell": "\f152",
|
||||||
"enter": "\f153",
|
"sell-outline": "\f153",
|
||||||
"expand-modal": "\f154",
|
"select": "\f154",
|
||||||
"expand": "\f155",
|
"search": "\f155",
|
||||||
"eye-crossed-outline": "\f156",
|
"sd-photo": "\f156",
|
||||||
"eye-crossed": "\f157",
|
"scheduled": "\f157",
|
||||||
"eye-outline": "\f158",
|
"schedule": "\f158",
|
||||||
"eye": "\f159",
|
"saved-messages": "\f159",
|
||||||
"favorite-filled": "\f15a",
|
"save-story": "\f15a",
|
||||||
"favorite": "\f15b",
|
"revote": "\f15b",
|
||||||
"file-badge": "\f15c",
|
"revenue-split": "\f15c",
|
||||||
"flag": "\f15d",
|
"reply": "\f15d",
|
||||||
"folder-badge": "\f15e",
|
"reply-filled": "\f15e",
|
||||||
"folder-tabs-bot": "\f15f",
|
"replies": "\f15f",
|
||||||
"folder-tabs-channel": "\f160",
|
"replace": "\f160",
|
||||||
"folder-tabs-chat": "\f161",
|
"reorder-tabs": "\f161",
|
||||||
"folder-tabs-chats": "\f162",
|
"reopen-topic": "\f162",
|
||||||
"folder-tabs-folder": "\f163",
|
"remove": "\f163",
|
||||||
"folder-tabs-group": "\f164",
|
"remove-quote": "\f164",
|
||||||
"folder-tabs-star": "\f165",
|
"reload": "\f165",
|
||||||
"folder-tabs-user": "\f166",
|
"refund": "\f166",
|
||||||
"folder": "\f167",
|
"recent": "\f167",
|
||||||
"fontsize": "\f168",
|
"readchats": "\f168",
|
||||||
"forums": "\f169",
|
"radial-badge": "\f169",
|
||||||
"forward": "\f16a",
|
"quote": "\f16a",
|
||||||
"fragment": "\f16b",
|
"quote-text": "\f16b",
|
||||||
"frozen-time": "\f16c",
|
"proof-of-ownership": "\f16c",
|
||||||
"fullscreen": "\f16d",
|
"privacy-policy": "\f16d",
|
||||||
"gifs": "\f16e",
|
"previous": "\f16e",
|
||||||
"gift-transfer-inline": "\f16f",
|
"poll": "\f16f",
|
||||||
"gift": "\f170",
|
"play": "\f170",
|
||||||
"group-filled": "\f171",
|
"play-story": "\f171",
|
||||||
"group": "\f172",
|
"pip": "\f172",
|
||||||
"grouped-disable": "\f173",
|
"pinned-message": "\f173",
|
||||||
"grouped": "\f174",
|
"pinned-chat": "\f174",
|
||||||
"hand-stop": "\f175",
|
"pin": "\f175",
|
||||||
"hashtag": "\f176",
|
"pin-list": "\f176",
|
||||||
"hd-photo": "\f177",
|
"pin-badge": "\f177",
|
||||||
"heart-outline": "\f178",
|
"photo": "\f178",
|
||||||
"heart": "\f179",
|
"phone": "\f179",
|
||||||
"help": "\f17a",
|
"phone-discard": "\f17a",
|
||||||
"info-filled": "\f17b",
|
"phone-discard-outline": "\f17b",
|
||||||
"info": "\f17c",
|
"permissions": "\f17c",
|
||||||
"install": "\f17d",
|
"pause": "\f17d",
|
||||||
"italic": "\f17e",
|
"password-off": "\f17e",
|
||||||
"key": "\f17f",
|
"open-in-new-tab": "\f17f",
|
||||||
"keyboard": "\f180",
|
"one-filled": "\f180",
|
||||||
"lamp": "\f181",
|
"note": "\f181",
|
||||||
"language": "\f182",
|
"non-contacts": "\f182",
|
||||||
"large-pause": "\f183",
|
"noise-suppression": "\f183",
|
||||||
"large-play": "\f184",
|
"nochannel": "\f184",
|
||||||
"link-badge": "\f185",
|
"next": "\f185",
|
||||||
"link-broken": "\f186",
|
"next-link": "\f186",
|
||||||
"link": "\f187",
|
"new-chat-filled": "\f187",
|
||||||
"location": "\f188",
|
"my-notes": "\f188",
|
||||||
"lock-badge": "\f189",
|
"muted": "\f189",
|
||||||
"lock": "\f18a",
|
"mute": "\f18a",
|
||||||
"logout": "\f18b",
|
"move-caption-up": "\f18b",
|
||||||
"loop": "\f18c",
|
"move-caption-down": "\f18c",
|
||||||
"mention": "\f18d",
|
"more": "\f18d",
|
||||||
"menu": "\f18e",
|
"more-circle": "\f18e",
|
||||||
"message-failed": "\f18f",
|
"monospace": "\f18f",
|
||||||
"message-pending": "\f190",
|
"microphone": "\f190",
|
||||||
"message-read": "\f191",
|
"microphone-alt": "\f191",
|
||||||
"message-succeeded": "\f192",
|
"message": "\f192",
|
||||||
"message": "\f193",
|
"message-succeeded": "\f193",
|
||||||
"microphone-alt": "\f194",
|
"message-read": "\f194",
|
||||||
"microphone": "\f195",
|
"message-pending": "\f195",
|
||||||
"monospace": "\f196",
|
"message-failed": "\f196",
|
||||||
"more-circle": "\f197",
|
"menu": "\f197",
|
||||||
"more": "\f198",
|
"mention": "\f198",
|
||||||
"move-caption-down": "\f199",
|
"loop": "\f199",
|
||||||
"move-caption-up": "\f19a",
|
"logout": "\f19a",
|
||||||
"mute": "\f19b",
|
"lock": "\f19b",
|
||||||
"muted": "\f19c",
|
"lock-badge": "\f19c",
|
||||||
"my-notes": "\f19d",
|
"location": "\f19d",
|
||||||
"new-chat-filled": "\f19e",
|
"link": "\f19e",
|
||||||
"next-link": "\f19f",
|
"link-broken": "\f19f",
|
||||||
"next": "\f1a0",
|
"link-badge": "\f1a0",
|
||||||
"nochannel": "\f1a1",
|
"large-play": "\f1a1",
|
||||||
"noise-suppression": "\f1a2",
|
"large-pause": "\f1a2",
|
||||||
"non-contacts": "\f1a3",
|
"language": "\f1a3",
|
||||||
"note": "\f1a4",
|
"lamp": "\f1a4",
|
||||||
"one-filled": "\f1a5",
|
"keyboard": "\f1a5",
|
||||||
"open-in-new-tab": "\f1a6",
|
"key": "\f1a6",
|
||||||
"password-off": "\f1a7",
|
"italic": "\f1a7",
|
||||||
"pause": "\f1a8",
|
"install": "\f1a8",
|
||||||
"permissions": "\f1a9",
|
"info": "\f1a9",
|
||||||
"phone-discard-outline": "\f1aa",
|
"info-filled": "\f1aa",
|
||||||
"phone-discard": "\f1ab",
|
"help": "\f1ab",
|
||||||
"phone": "\f1ac",
|
"heart": "\f1ac",
|
||||||
"photo": "\f1ad",
|
"heart-outline": "\f1ad",
|
||||||
"pin-badge": "\f1ae",
|
"hd-photo": "\f1ae",
|
||||||
"pin-list": "\f1af",
|
"hashtag": "\f1af",
|
||||||
"pin": "\f1b0",
|
"hand-stop": "\f1b0",
|
||||||
"pinned-chat": "\f1b1",
|
"grouped": "\f1b1",
|
||||||
"pinned-message": "\f1b2",
|
"grouped-disable": "\f1b2",
|
||||||
"pip": "\f1b3",
|
"group": "\f1b3",
|
||||||
"play-story": "\f1b4",
|
"group-filled": "\f1b4",
|
||||||
"play": "\f1b5",
|
"gift": "\f1b5",
|
||||||
"poll": "\f1b6",
|
"gift-transfer-inline": "\f1b6",
|
||||||
"previous": "\f1b7",
|
"gifs": "\f1b7",
|
||||||
"privacy-policy": "\f1b8",
|
"fullscreen": "\f1b8",
|
||||||
"proof-of-ownership": "\f1b9",
|
"frozen-time": "\f1b9",
|
||||||
"quote-text": "\f1ba",
|
"fragment": "\f1ba",
|
||||||
"quote": "\f1bb",
|
"forward": "\f1bb",
|
||||||
"radial-badge": "\f1bc",
|
"forums": "\f1bc",
|
||||||
"rating-icons-level1": "\f1bd",
|
"fontsize": "\f1bd",
|
||||||
"rating-icons-level10": "\f1be",
|
"folder": "\f1be",
|
||||||
"rating-icons-level2": "\f1bf",
|
"folder-badge": "\f1bf",
|
||||||
"rating-icons-level20": "\f1c0",
|
"flag": "\f1c0",
|
||||||
"rating-icons-level3": "\f1c1",
|
"file-badge": "\f1c1",
|
||||||
"rating-icons-level30": "\f1c2",
|
"favorite": "\f1c2",
|
||||||
"rating-icons-level4": "\f1c3",
|
"favorite-filled": "\f1c3",
|
||||||
"rating-icons-level40": "\f1c4",
|
"eye": "\f1c4",
|
||||||
"rating-icons-level5": "\f1c5",
|
"eye-outline": "\f1c5",
|
||||||
"rating-icons-level50": "\f1c6",
|
"eye-crossed": "\f1c6",
|
||||||
"rating-icons-level6": "\f1c7",
|
"eye-crossed-outline": "\f1c7",
|
||||||
"rating-icons-level60": "\f1c8",
|
"expand": "\f1c8",
|
||||||
"rating-icons-level7": "\f1c9",
|
"expand-modal": "\f1c9",
|
||||||
"rating-icons-level70": "\f1ca",
|
"enter": "\f1ca",
|
||||||
"rating-icons-level8": "\f1cb",
|
"email": "\f1cb",
|
||||||
"rating-icons-level80": "\f1cc",
|
"edit": "\f1cc",
|
||||||
"rating-icons-level9": "\f1cd",
|
"eats": "\f1cd",
|
||||||
"rating-icons-level90": "\f1ce",
|
"dropdown-arrows": "\f1ce",
|
||||||
"rating-icons-negative": "\f1cf",
|
"download": "\f1cf",
|
||||||
"readchats": "\f1d0",
|
"down": "\f1d0",
|
||||||
"recent": "\f1d1",
|
"double-badge": "\f1d1",
|
||||||
"refund": "\f1d2",
|
"document": "\f1d2",
|
||||||
"reload": "\f1d3",
|
"diamond": "\f1d3",
|
||||||
"remove-quote": "\f1d4",
|
"delete": "\f1d4",
|
||||||
"remove": "\f1d5",
|
"delete-user": "\f1d5",
|
||||||
"reopen-topic": "\f1d6",
|
"delete-left": "\f1d6",
|
||||||
"reorder-tabs": "\f1d7",
|
"delete-filled": "\f1d7",
|
||||||
"replace": "\f1d8",
|
"data": "\f1d8",
|
||||||
"replies": "\f1d9",
|
"darkmode": "\f1d9",
|
||||||
"reply-filled": "\f1da",
|
"crown-wear": "\f1da",
|
||||||
"reply": "\f1db",
|
"crown-wear-outline": "\f1db",
|
||||||
"revenue-split": "\f1dc",
|
"crown-take-off": "\f1dc",
|
||||||
"revote": "\f1dd",
|
"crown-take-off-outline": "\f1dd",
|
||||||
"save-story": "\f1de",
|
"craft": "\f1de",
|
||||||
"saved-messages": "\f1df",
|
"copy": "\f1df",
|
||||||
"schedule": "\f1e0",
|
"copy-media": "\f1e0",
|
||||||
"scheduled": "\f1e1",
|
"comments": "\f1e1",
|
||||||
"sd-photo": "\f1e2",
|
"comments-sticker": "\f1e2",
|
||||||
"search": "\f1e3",
|
"combine-craft": "\f1e3",
|
||||||
"select": "\f1e4",
|
"colorize": "\f1e4",
|
||||||
"sell-outline": "\f1e5",
|
"collapse": "\f1e5",
|
||||||
"sell": "\f1e6",
|
"collapse-modal": "\f1e6",
|
||||||
"send-outline": "\f1e7",
|
"cloud-download": "\f1e7",
|
||||||
"send": "\f1e8",
|
"closed-gift": "\f1e8",
|
||||||
"settings-filled": "\f1e9",
|
"close": "\f1e9",
|
||||||
"settings": "\f1ea",
|
"close-topic": "\f1ea",
|
||||||
"share-filled": "\f1eb",
|
"close-circle": "\f1eb",
|
||||||
"share-screen-outlined": "\f1ec",
|
"clock": "\f1ec",
|
||||||
"share-screen-stop": "\f1ed",
|
"clock-edit": "\f1ed",
|
||||||
"share-screen": "\f1ee",
|
"check": "\f1ee",
|
||||||
"show-message": "\f1ef",
|
"chats-badge": "\f1ef",
|
||||||
"sidebar": "\f1f0",
|
"chat-badge": "\f1f0",
|
||||||
"skip-next": "\f1f1",
|
"channelviews": "\f1f1",
|
||||||
"skip-previous": "\f1f2",
|
"channel": "\f1f2",
|
||||||
"smallscreen": "\f1f3",
|
"channel-filled": "\f1f3",
|
||||||
"smile": "\f1f4",
|
"cash-circle": "\f1f4",
|
||||||
"sort-by-date": "\f1f5",
|
"card": "\f1f5",
|
||||||
"sort-by-number": "\f1f6",
|
"car": "\f1f6",
|
||||||
"sort-by-price": "\f1f7",
|
"camera": "\f1f7",
|
||||||
"sort": "\f1f8",
|
"camera-add": "\f1f8",
|
||||||
"speaker-muted-story": "\f1f9",
|
"calendar": "\f1f9",
|
||||||
"speaker-outline": "\f1fa",
|
"calendar-filter": "\f1fa",
|
||||||
"speaker-story": "\f1fb",
|
"bug": "\f1fb",
|
||||||
"speaker": "\f1fc",
|
"bots": "\f1fc",
|
||||||
"spoiler-disable": "\f1fd",
|
"bot-commands-filled": "\f1fd",
|
||||||
"spoiler": "\f1fe",
|
"bot-command": "\f1fe",
|
||||||
"sport": "\f1ff",
|
"boosts": "\f1ff",
|
||||||
"star": "\f200",
|
"boostcircle": "\f200",
|
||||||
"stars-lock": "\f201",
|
"boost": "\f201",
|
||||||
"stars-refund": "\f202",
|
"boost-outline": "\f202",
|
||||||
"stats": "\f203",
|
"boost-craft-chance": "\f203",
|
||||||
"stealth-future": "\f204",
|
"bold": "\f204",
|
||||||
"stealth-past": "\f205",
|
"avatar-saved-messages": "\f205",
|
||||||
"stickers": "\f206",
|
"avatar-deleted-account": "\f206",
|
||||||
"stop-raising-hand": "\f207",
|
"avatar-archived-chats": "\f207",
|
||||||
"stop": "\f208",
|
"author-hidden": "\f208",
|
||||||
"story-caption": "\f209",
|
"auction": "\f209",
|
||||||
"story-expired": "\f20a",
|
"auction-next-round": "\f20a",
|
||||||
"story-priority": "\f20b",
|
"auction-filled": "\f20b",
|
||||||
"story-reply": "\f20c",
|
"auction-drop": "\f20c",
|
||||||
"strikethrough": "\f20d",
|
"attach": "\f20d",
|
||||||
"tag-add": "\f20e",
|
"ask-support": "\f20e",
|
||||||
"tag-crossed": "\f20f",
|
"arrow-right": "\f20f",
|
||||||
"tag-filter": "\f210",
|
"arrow-left": "\f210",
|
||||||
"tag-name": "\f211",
|
"arrow-down": "\f211",
|
||||||
"tag": "\f212",
|
"arrow-down-circle": "\f212",
|
||||||
"timer": "\f213",
|
"archive": "\f213",
|
||||||
"toncoin": "\f214",
|
"archive-to-main": "\f214",
|
||||||
"tools": "\f215",
|
"archive-from-main": "\f215",
|
||||||
"topic-new": "\f216",
|
"archive-filled": "\f216",
|
||||||
"trade": "\f217",
|
"animations": "\f217",
|
||||||
"transcribe": "\f218",
|
"animals": "\f218",
|
||||||
"truck": "\f219",
|
"allow-speak": "\f219",
|
||||||
"unarchive": "\f21a",
|
"admin": "\f21a",
|
||||||
"underlined": "\f21b",
|
"add": "\f21b",
|
||||||
"understood": "\f21c",
|
"add-user": "\f21c",
|
||||||
"unique-profile": "\f21d",
|
"add-user-filled": "\f21d",
|
||||||
"unlist-outline": "\f21e",
|
"add-one-badge": "\f21e",
|
||||||
"unlist": "\f21f",
|
"add-filled": "\f21f",
|
||||||
"unlock-badge": "\f220",
|
"active-sessions": "\f220",
|
||||||
"unlock": "\f221",
|
"rating-icons-negative": "\f221",
|
||||||
"unmute": "\f222",
|
"rating-icons-level90": "\f222",
|
||||||
"unpin": "\f223",
|
"rating-icons-level9": "\f223",
|
||||||
"unread": "\f224",
|
"rating-icons-level80": "\f224",
|
||||||
"up": "\f225",
|
"rating-icons-level8": "\f225",
|
||||||
"user-filled": "\f226",
|
"rating-icons-level70": "\f226",
|
||||||
"user-online": "\f227",
|
"rating-icons-level7": "\f227",
|
||||||
"user-stars": "\f228",
|
"rating-icons-level60": "\f228",
|
||||||
"user": "\f229",
|
"rating-icons-level6": "\f229",
|
||||||
"video-outlined": "\f22a",
|
"rating-icons-level50": "\f22a",
|
||||||
"video-stop": "\f22b",
|
"rating-icons-level5": "\f22b",
|
||||||
"video": "\f22c",
|
"rating-icons-level40": "\f22c",
|
||||||
"view-once": "\f22d",
|
"rating-icons-level4": "\f22d",
|
||||||
"voice-chat": "\f22e",
|
"rating-icons-level30": "\f22e",
|
||||||
"volume-1": "\f22f",
|
"rating-icons-level3": "\f22f",
|
||||||
"volume-2": "\f230",
|
"rating-icons-level20": "\f230",
|
||||||
"volume-3": "\f231",
|
"rating-icons-level2": "\f231",
|
||||||
"warning": "\f232",
|
"rating-icons-level10": "\f232",
|
||||||
"web": "\f233",
|
"rating-icons-level1": "\f233",
|
||||||
"webapp": "\f234",
|
"folder-tabs-user": "\f234",
|
||||||
"word-wrap": "\f235",
|
"folder-tabs-star": "\f235",
|
||||||
"zoom-in": "\f236",
|
"folder-tabs-group": "\f236",
|
||||||
"zoom-out": "\f237",
|
"folder-tabs-folder": "\f237",
|
||||||
|
"folder-tabs-chats": "\f238",
|
||||||
|
"folder-tabs-chat": "\f239",
|
||||||
|
"folder-tabs-channel": "\f23a",
|
||||||
|
"folder-tabs-bot": "\f23b",
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,312 +1,316 @@
|
|||||||
export type FontIconName =
|
export type FontIconName =
|
||||||
| 'active-sessions'
|
| 'zoom-out'
|
||||||
| 'add-one-badge'
|
|
||||||
| 'add-user-filled'
|
|
||||||
| 'add-user'
|
|
||||||
| 'add'
|
|
||||||
| 'admin'
|
|
||||||
| 'allow-speak'
|
|
||||||
| 'animals'
|
|
||||||
| 'animations'
|
|
||||||
| 'archive-filled'
|
|
||||||
| 'archive-from-main'
|
|
||||||
| 'archive-to-main'
|
|
||||||
| 'archive'
|
|
||||||
| 'arrow-down-circle'
|
|
||||||
| 'arrow-down'
|
|
||||||
| 'arrow-left'
|
|
||||||
| 'arrow-right'
|
|
||||||
| 'ask-support'
|
|
||||||
| 'attach'
|
|
||||||
| 'auction-drop'
|
|
||||||
| 'auction-filled'
|
|
||||||
| 'auction-next-round'
|
|
||||||
| 'auction'
|
|
||||||
| 'author-hidden'
|
|
||||||
| 'avatar-archived-chats'
|
|
||||||
| 'avatar-deleted-account'
|
|
||||||
| 'avatar-saved-messages'
|
|
||||||
| 'bold'
|
|
||||||
| 'boost-outline'
|
|
||||||
| 'boost'
|
|
||||||
| 'boostcircle'
|
|
||||||
| 'boosts'
|
|
||||||
| 'bot-command'
|
|
||||||
| 'bot-commands-filled'
|
|
||||||
| 'bots'
|
|
||||||
| 'bug'
|
|
||||||
| 'calendar-filter'
|
|
||||||
| 'calendar'
|
|
||||||
| 'camera-add'
|
|
||||||
| 'camera'
|
|
||||||
| 'car'
|
|
||||||
| 'card'
|
|
||||||
| 'cash-circle'
|
|
||||||
| 'channel-filled'
|
|
||||||
| 'channel'
|
|
||||||
| 'channelviews'
|
|
||||||
| 'chat-badge'
|
|
||||||
| 'chats-badge'
|
|
||||||
| 'check'
|
|
||||||
| 'clock-edit'
|
|
||||||
| 'clock'
|
|
||||||
| 'close-circle'
|
|
||||||
| 'close-topic'
|
|
||||||
| 'close'
|
|
||||||
| 'closed-gift'
|
|
||||||
| 'cloud-download'
|
|
||||||
| 'collapse-modal'
|
|
||||||
| 'collapse'
|
|
||||||
| 'colorize'
|
|
||||||
| 'comments-sticker'
|
|
||||||
| 'comments'
|
|
||||||
| 'copy-media'
|
|
||||||
| 'copy'
|
|
||||||
| 'crown-take-off-outline'
|
|
||||||
| 'crown-take-off'
|
|
||||||
| 'crown-wear-outline'
|
|
||||||
| 'crown-wear'
|
|
||||||
| 'darkmode'
|
|
||||||
| 'data'
|
|
||||||
| 'delete-filled'
|
|
||||||
| 'delete-left'
|
|
||||||
| 'delete-user'
|
|
||||||
| 'delete'
|
|
||||||
| 'diamond'
|
|
||||||
| 'document'
|
|
||||||
| 'double-badge'
|
|
||||||
| 'down'
|
|
||||||
| 'download'
|
|
||||||
| 'dropdown-arrows'
|
|
||||||
| 'eats'
|
|
||||||
| 'edit'
|
|
||||||
| 'email'
|
|
||||||
| 'enter'
|
|
||||||
| 'expand-modal'
|
|
||||||
| 'expand'
|
|
||||||
| 'eye-crossed-outline'
|
|
||||||
| 'eye-crossed'
|
|
||||||
| 'eye-outline'
|
|
||||||
| 'eye'
|
|
||||||
| 'favorite-filled'
|
|
||||||
| 'favorite'
|
|
||||||
| 'file-badge'
|
|
||||||
| 'flag'
|
|
||||||
| 'folder-badge'
|
|
||||||
| 'folder-tabs-bot'
|
|
||||||
| 'folder-tabs-channel'
|
|
||||||
| 'folder-tabs-chat'
|
|
||||||
| 'folder-tabs-chats'
|
|
||||||
| 'folder-tabs-folder'
|
|
||||||
| 'folder-tabs-group'
|
|
||||||
| 'folder-tabs-star'
|
|
||||||
| 'folder-tabs-user'
|
|
||||||
| 'folder'
|
|
||||||
| 'fontsize'
|
|
||||||
| 'forums'
|
|
||||||
| 'forward'
|
|
||||||
| 'fragment'
|
|
||||||
| 'frozen-time'
|
|
||||||
| 'fullscreen'
|
|
||||||
| 'gifs'
|
|
||||||
| 'gift-transfer-inline'
|
|
||||||
| 'gift'
|
|
||||||
| 'group-filled'
|
|
||||||
| 'group'
|
|
||||||
| 'grouped-disable'
|
|
||||||
| 'grouped'
|
|
||||||
| 'hand-stop'
|
|
||||||
| 'hashtag'
|
|
||||||
| 'hd-photo'
|
|
||||||
| 'heart-outline'
|
|
||||||
| 'heart'
|
|
||||||
| 'help'
|
|
||||||
| 'info-filled'
|
|
||||||
| 'info'
|
|
||||||
| 'install'
|
|
||||||
| 'italic'
|
|
||||||
| 'key'
|
|
||||||
| 'keyboard'
|
|
||||||
| 'lamp'
|
|
||||||
| 'language'
|
|
||||||
| 'large-pause'
|
|
||||||
| 'large-play'
|
|
||||||
| 'link-badge'
|
|
||||||
| 'link-broken'
|
|
||||||
| 'link'
|
|
||||||
| 'location'
|
|
||||||
| 'lock-badge'
|
|
||||||
| 'lock'
|
|
||||||
| 'logout'
|
|
||||||
| 'loop'
|
|
||||||
| 'mention'
|
|
||||||
| 'menu'
|
|
||||||
| 'message-failed'
|
|
||||||
| 'message-pending'
|
|
||||||
| 'message-read'
|
|
||||||
| 'message-succeeded'
|
|
||||||
| 'message'
|
|
||||||
| 'microphone-alt'
|
|
||||||
| 'microphone'
|
|
||||||
| 'monospace'
|
|
||||||
| 'more-circle'
|
|
||||||
| 'more'
|
|
||||||
| 'move-caption-down'
|
|
||||||
| 'move-caption-up'
|
|
||||||
| 'mute'
|
|
||||||
| 'muted'
|
|
||||||
| 'my-notes'
|
|
||||||
| 'new-chat-filled'
|
|
||||||
| 'next-link'
|
|
||||||
| 'next'
|
|
||||||
| 'nochannel'
|
|
||||||
| 'noise-suppression'
|
|
||||||
| 'non-contacts'
|
|
||||||
| 'note'
|
|
||||||
| 'one-filled'
|
|
||||||
| 'open-in-new-tab'
|
|
||||||
| 'password-off'
|
|
||||||
| 'pause'
|
|
||||||
| 'permissions'
|
|
||||||
| 'phone-discard-outline'
|
|
||||||
| 'phone-discard'
|
|
||||||
| 'phone'
|
|
||||||
| 'photo'
|
|
||||||
| 'pin-badge'
|
|
||||||
| 'pin-list'
|
|
||||||
| 'pin'
|
|
||||||
| 'pinned-chat'
|
|
||||||
| 'pinned-message'
|
|
||||||
| 'pip'
|
|
||||||
| 'play-story'
|
|
||||||
| 'play'
|
|
||||||
| 'poll'
|
|
||||||
| 'previous'
|
|
||||||
| 'privacy-policy'
|
|
||||||
| 'proof-of-ownership'
|
|
||||||
| 'quote-text'
|
|
||||||
| 'quote'
|
|
||||||
| 'radial-badge'
|
|
||||||
| 'rating-icons-level1'
|
|
||||||
| 'rating-icons-level10'
|
|
||||||
| 'rating-icons-level2'
|
|
||||||
| 'rating-icons-level20'
|
|
||||||
| 'rating-icons-level3'
|
|
||||||
| 'rating-icons-level30'
|
|
||||||
| 'rating-icons-level4'
|
|
||||||
| 'rating-icons-level40'
|
|
||||||
| 'rating-icons-level5'
|
|
||||||
| 'rating-icons-level50'
|
|
||||||
| 'rating-icons-level6'
|
|
||||||
| 'rating-icons-level60'
|
|
||||||
| 'rating-icons-level7'
|
|
||||||
| 'rating-icons-level70'
|
|
||||||
| 'rating-icons-level8'
|
|
||||||
| 'rating-icons-level80'
|
|
||||||
| 'rating-icons-level9'
|
|
||||||
| 'rating-icons-level90'
|
|
||||||
| 'rating-icons-negative'
|
|
||||||
| 'readchats'
|
|
||||||
| 'recent'
|
|
||||||
| 'refund'
|
|
||||||
| 'reload'
|
|
||||||
| 'remove-quote'
|
|
||||||
| 'remove'
|
|
||||||
| 'reopen-topic'
|
|
||||||
| 'reorder-tabs'
|
|
||||||
| 'replace'
|
|
||||||
| 'replies'
|
|
||||||
| 'reply-filled'
|
|
||||||
| 'reply'
|
|
||||||
| 'revenue-split'
|
|
||||||
| 'revote'
|
|
||||||
| 'save-story'
|
|
||||||
| 'saved-messages'
|
|
||||||
| 'schedule'
|
|
||||||
| 'scheduled'
|
|
||||||
| 'sd-photo'
|
|
||||||
| 'search'
|
|
||||||
| 'select'
|
|
||||||
| 'sell-outline'
|
|
||||||
| 'sell'
|
|
||||||
| 'send-outline'
|
|
||||||
| 'send'
|
|
||||||
| 'settings-filled'
|
|
||||||
| 'settings'
|
|
||||||
| 'share-filled'
|
|
||||||
| 'share-screen-outlined'
|
|
||||||
| 'share-screen-stop'
|
|
||||||
| 'share-screen'
|
|
||||||
| 'show-message'
|
|
||||||
| 'sidebar'
|
|
||||||
| 'skip-next'
|
|
||||||
| 'skip-previous'
|
|
||||||
| 'smallscreen'
|
|
||||||
| 'smile'
|
|
||||||
| 'sort-by-date'
|
|
||||||
| 'sort-by-number'
|
|
||||||
| 'sort-by-price'
|
|
||||||
| 'sort'
|
|
||||||
| 'speaker-muted-story'
|
|
||||||
| 'speaker-outline'
|
|
||||||
| 'speaker-story'
|
|
||||||
| 'speaker'
|
|
||||||
| 'spoiler-disable'
|
|
||||||
| 'spoiler'
|
|
||||||
| 'sport'
|
|
||||||
| 'star'
|
|
||||||
| 'stars-lock'
|
|
||||||
| 'stars-refund'
|
|
||||||
| 'stats'
|
|
||||||
| 'stealth-future'
|
|
||||||
| 'stealth-past'
|
|
||||||
| 'stickers'
|
|
||||||
| 'stop-raising-hand'
|
|
||||||
| 'stop'
|
|
||||||
| 'story-caption'
|
|
||||||
| 'story-expired'
|
|
||||||
| 'story-priority'
|
|
||||||
| 'story-reply'
|
|
||||||
| 'strikethrough'
|
|
||||||
| 'tag-add'
|
|
||||||
| 'tag-crossed'
|
|
||||||
| 'tag-filter'
|
|
||||||
| 'tag-name'
|
|
||||||
| 'tag'
|
|
||||||
| 'timer'
|
|
||||||
| 'toncoin'
|
|
||||||
| 'tools'
|
|
||||||
| 'topic-new'
|
|
||||||
| 'trade'
|
|
||||||
| 'transcribe'
|
|
||||||
| 'truck'
|
|
||||||
| 'unarchive'
|
|
||||||
| 'underlined'
|
|
||||||
| 'understood'
|
|
||||||
| 'unique-profile'
|
|
||||||
| 'unlist-outline'
|
|
||||||
| 'unlist'
|
|
||||||
| 'unlock-badge'
|
|
||||||
| 'unlock'
|
|
||||||
| 'unmute'
|
|
||||||
| 'unpin'
|
|
||||||
| 'unread'
|
|
||||||
| 'up'
|
|
||||||
| 'user-filled'
|
|
||||||
| 'user-online'
|
|
||||||
| 'user-stars'
|
|
||||||
| 'user'
|
|
||||||
| 'video-outlined'
|
|
||||||
| 'video-stop'
|
|
||||||
| 'video'
|
|
||||||
| 'view-once'
|
|
||||||
| 'voice-chat'
|
|
||||||
| 'volume-1'
|
|
||||||
| 'volume-2'
|
|
||||||
| 'volume-3'
|
|
||||||
| 'warning'
|
|
||||||
| 'web'
|
|
||||||
| 'webapp'
|
|
||||||
| 'word-wrap'
|
|
||||||
| 'zoom-in'
|
| 'zoom-in'
|
||||||
| 'zoom-out';
|
| 'word-wrap'
|
||||||
|
| 'webapp'
|
||||||
|
| 'web'
|
||||||
|
| 'warning'
|
||||||
|
| 'volume-3'
|
||||||
|
| 'volume-2'
|
||||||
|
| 'volume-1'
|
||||||
|
| 'voice-chat'
|
||||||
|
| 'view-once'
|
||||||
|
| 'video'
|
||||||
|
| 'video-stop'
|
||||||
|
| 'video-outlined'
|
||||||
|
| 'user'
|
||||||
|
| 'user-stars'
|
||||||
|
| 'user-online'
|
||||||
|
| 'user-filled'
|
||||||
|
| 'up'
|
||||||
|
| 'unread'
|
||||||
|
| 'unpin'
|
||||||
|
| 'unmute'
|
||||||
|
| 'unlock'
|
||||||
|
| 'unlock-badge'
|
||||||
|
| 'unlist'
|
||||||
|
| 'unlist-outline'
|
||||||
|
| 'unique-profile'
|
||||||
|
| 'understood'
|
||||||
|
| 'underlined'
|
||||||
|
| 'unarchive'
|
||||||
|
| 'truck'
|
||||||
|
| 'transcribe'
|
||||||
|
| 'trade'
|
||||||
|
| 'topic-new'
|
||||||
|
| 'tools'
|
||||||
|
| 'toncoin'
|
||||||
|
| 'timer'
|
||||||
|
| 'tag'
|
||||||
|
| 'tag-name'
|
||||||
|
| 'tag-filter'
|
||||||
|
| 'tag-crossed'
|
||||||
|
| 'tag-add'
|
||||||
|
| 'strikethrough'
|
||||||
|
| 'story-reply'
|
||||||
|
| 'story-priority'
|
||||||
|
| 'story-expired'
|
||||||
|
| 'story-caption'
|
||||||
|
| 'stop'
|
||||||
|
| 'stop-raising-hand'
|
||||||
|
| 'stickers'
|
||||||
|
| 'stealth-past'
|
||||||
|
| 'stealth-future'
|
||||||
|
| 'stats'
|
||||||
|
| 'stars-refund'
|
||||||
|
| 'stars-lock'
|
||||||
|
| 'star'
|
||||||
|
| 'sport'
|
||||||
|
| 'spoiler'
|
||||||
|
| 'spoiler-disable'
|
||||||
|
| 'speaker'
|
||||||
|
| 'speaker-story'
|
||||||
|
| 'speaker-outline'
|
||||||
|
| 'speaker-muted-story'
|
||||||
|
| 'sort'
|
||||||
|
| 'sort-by-price'
|
||||||
|
| 'sort-by-number'
|
||||||
|
| 'sort-by-date'
|
||||||
|
| 'smile'
|
||||||
|
| 'smallscreen'
|
||||||
|
| 'skip-previous'
|
||||||
|
| 'skip-next'
|
||||||
|
| 'sidebar'
|
||||||
|
| 'show-message'
|
||||||
|
| 'share-screen'
|
||||||
|
| 'share-screen-stop'
|
||||||
|
| 'share-screen-outlined'
|
||||||
|
| 'share-filled'
|
||||||
|
| 'settings'
|
||||||
|
| 'settings-filled'
|
||||||
|
| 'send'
|
||||||
|
| 'send-outline'
|
||||||
|
| 'sell'
|
||||||
|
| 'sell-outline'
|
||||||
|
| 'select'
|
||||||
|
| 'search'
|
||||||
|
| 'sd-photo'
|
||||||
|
| 'scheduled'
|
||||||
|
| 'schedule'
|
||||||
|
| 'saved-messages'
|
||||||
|
| 'save-story'
|
||||||
|
| 'revote'
|
||||||
|
| 'revenue-split'
|
||||||
|
| 'reply'
|
||||||
|
| 'reply-filled'
|
||||||
|
| 'replies'
|
||||||
|
| 'replace'
|
||||||
|
| 'reorder-tabs'
|
||||||
|
| 'reopen-topic'
|
||||||
|
| 'remove'
|
||||||
|
| 'remove-quote'
|
||||||
|
| 'reload'
|
||||||
|
| 'refund'
|
||||||
|
| 'recent'
|
||||||
|
| 'readchats'
|
||||||
|
| 'radial-badge'
|
||||||
|
| 'quote'
|
||||||
|
| 'quote-text'
|
||||||
|
| 'proof-of-ownership'
|
||||||
|
| 'privacy-policy'
|
||||||
|
| 'previous'
|
||||||
|
| 'poll'
|
||||||
|
| 'play'
|
||||||
|
| 'play-story'
|
||||||
|
| 'pip'
|
||||||
|
| 'pinned-message'
|
||||||
|
| 'pinned-chat'
|
||||||
|
| 'pin'
|
||||||
|
| 'pin-list'
|
||||||
|
| 'pin-badge'
|
||||||
|
| 'photo'
|
||||||
|
| 'phone'
|
||||||
|
| 'phone-discard'
|
||||||
|
| 'phone-discard-outline'
|
||||||
|
| 'permissions'
|
||||||
|
| 'pause'
|
||||||
|
| 'password-off'
|
||||||
|
| 'open-in-new-tab'
|
||||||
|
| 'one-filled'
|
||||||
|
| 'note'
|
||||||
|
| 'non-contacts'
|
||||||
|
| 'noise-suppression'
|
||||||
|
| 'nochannel'
|
||||||
|
| 'next'
|
||||||
|
| 'next-link'
|
||||||
|
| 'new-chat-filled'
|
||||||
|
| 'my-notes'
|
||||||
|
| 'muted'
|
||||||
|
| 'mute'
|
||||||
|
| 'move-caption-up'
|
||||||
|
| 'move-caption-down'
|
||||||
|
| 'more'
|
||||||
|
| 'more-circle'
|
||||||
|
| 'monospace'
|
||||||
|
| 'microphone'
|
||||||
|
| 'microphone-alt'
|
||||||
|
| 'message'
|
||||||
|
| 'message-succeeded'
|
||||||
|
| 'message-read'
|
||||||
|
| 'message-pending'
|
||||||
|
| 'message-failed'
|
||||||
|
| 'menu'
|
||||||
|
| 'mention'
|
||||||
|
| 'loop'
|
||||||
|
| 'logout'
|
||||||
|
| 'lock'
|
||||||
|
| 'lock-badge'
|
||||||
|
| 'location'
|
||||||
|
| 'link'
|
||||||
|
| 'link-broken'
|
||||||
|
| 'link-badge'
|
||||||
|
| 'large-play'
|
||||||
|
| 'large-pause'
|
||||||
|
| 'language'
|
||||||
|
| 'lamp'
|
||||||
|
| 'keyboard'
|
||||||
|
| 'key'
|
||||||
|
| 'italic'
|
||||||
|
| 'install'
|
||||||
|
| 'info'
|
||||||
|
| 'info-filled'
|
||||||
|
| 'help'
|
||||||
|
| 'heart'
|
||||||
|
| 'heart-outline'
|
||||||
|
| 'hd-photo'
|
||||||
|
| 'hashtag'
|
||||||
|
| 'hand-stop'
|
||||||
|
| 'grouped'
|
||||||
|
| 'grouped-disable'
|
||||||
|
| 'group'
|
||||||
|
| 'group-filled'
|
||||||
|
| 'gift'
|
||||||
|
| 'gift-transfer-inline'
|
||||||
|
| 'gifs'
|
||||||
|
| 'fullscreen'
|
||||||
|
| 'frozen-time'
|
||||||
|
| 'fragment'
|
||||||
|
| 'forward'
|
||||||
|
| 'forums'
|
||||||
|
| 'fontsize'
|
||||||
|
| 'folder'
|
||||||
|
| 'folder-badge'
|
||||||
|
| 'flag'
|
||||||
|
| 'file-badge'
|
||||||
|
| 'favorite'
|
||||||
|
| 'favorite-filled'
|
||||||
|
| 'eye'
|
||||||
|
| 'eye-outline'
|
||||||
|
| 'eye-crossed'
|
||||||
|
| 'eye-crossed-outline'
|
||||||
|
| 'expand'
|
||||||
|
| 'expand-modal'
|
||||||
|
| 'enter'
|
||||||
|
| 'email'
|
||||||
|
| 'edit'
|
||||||
|
| 'eats'
|
||||||
|
| 'dropdown-arrows'
|
||||||
|
| 'download'
|
||||||
|
| 'down'
|
||||||
|
| 'double-badge'
|
||||||
|
| 'document'
|
||||||
|
| 'diamond'
|
||||||
|
| 'delete'
|
||||||
|
| 'delete-user'
|
||||||
|
| 'delete-left'
|
||||||
|
| 'delete-filled'
|
||||||
|
| 'data'
|
||||||
|
| 'darkmode'
|
||||||
|
| 'crown-wear'
|
||||||
|
| 'crown-wear-outline'
|
||||||
|
| 'crown-take-off'
|
||||||
|
| 'crown-take-off-outline'
|
||||||
|
| 'craft'
|
||||||
|
| 'copy'
|
||||||
|
| 'copy-media'
|
||||||
|
| 'comments'
|
||||||
|
| 'comments-sticker'
|
||||||
|
| 'combine-craft'
|
||||||
|
| 'colorize'
|
||||||
|
| 'collapse'
|
||||||
|
| 'collapse-modal'
|
||||||
|
| 'cloud-download'
|
||||||
|
| 'closed-gift'
|
||||||
|
| 'close'
|
||||||
|
| 'close-topic'
|
||||||
|
| 'close-circle'
|
||||||
|
| 'clock'
|
||||||
|
| 'clock-edit'
|
||||||
|
| 'check'
|
||||||
|
| 'chats-badge'
|
||||||
|
| 'chat-badge'
|
||||||
|
| 'channelviews'
|
||||||
|
| 'channel'
|
||||||
|
| 'channel-filled'
|
||||||
|
| 'cash-circle'
|
||||||
|
| 'card'
|
||||||
|
| 'car'
|
||||||
|
| 'camera'
|
||||||
|
| 'camera-add'
|
||||||
|
| 'calendar'
|
||||||
|
| 'calendar-filter'
|
||||||
|
| 'bug'
|
||||||
|
| 'bots'
|
||||||
|
| 'bot-commands-filled'
|
||||||
|
| 'bot-command'
|
||||||
|
| 'boosts'
|
||||||
|
| 'boostcircle'
|
||||||
|
| 'boost'
|
||||||
|
| 'boost-outline'
|
||||||
|
| 'boost-craft-chance'
|
||||||
|
| 'bold'
|
||||||
|
| 'avatar-saved-messages'
|
||||||
|
| 'avatar-deleted-account'
|
||||||
|
| 'avatar-archived-chats'
|
||||||
|
| 'author-hidden'
|
||||||
|
| 'auction'
|
||||||
|
| 'auction-next-round'
|
||||||
|
| 'auction-filled'
|
||||||
|
| 'auction-drop'
|
||||||
|
| 'attach'
|
||||||
|
| 'ask-support'
|
||||||
|
| 'arrow-right'
|
||||||
|
| 'arrow-left'
|
||||||
|
| 'arrow-down'
|
||||||
|
| 'arrow-down-circle'
|
||||||
|
| 'archive'
|
||||||
|
| 'archive-to-main'
|
||||||
|
| 'archive-from-main'
|
||||||
|
| 'archive-filled'
|
||||||
|
| 'animations'
|
||||||
|
| 'animals'
|
||||||
|
| 'allow-speak'
|
||||||
|
| 'admin'
|
||||||
|
| 'add'
|
||||||
|
| 'add-user'
|
||||||
|
| 'add-user-filled'
|
||||||
|
| 'add-one-badge'
|
||||||
|
| 'add-filled'
|
||||||
|
| 'active-sessions'
|
||||||
|
| 'rating-icons-negative'
|
||||||
|
| 'rating-icons-level90'
|
||||||
|
| 'rating-icons-level9'
|
||||||
|
| 'rating-icons-level80'
|
||||||
|
| 'rating-icons-level8'
|
||||||
|
| 'rating-icons-level70'
|
||||||
|
| 'rating-icons-level7'
|
||||||
|
| 'rating-icons-level60'
|
||||||
|
| 'rating-icons-level6'
|
||||||
|
| 'rating-icons-level50'
|
||||||
|
| 'rating-icons-level5'
|
||||||
|
| 'rating-icons-level40'
|
||||||
|
| 'rating-icons-level4'
|
||||||
|
| 'rating-icons-level30'
|
||||||
|
| 'rating-icons-level3'
|
||||||
|
| 'rating-icons-level20'
|
||||||
|
| 'rating-icons-level2'
|
||||||
|
| 'rating-icons-level10'
|
||||||
|
| 'rating-icons-level1'
|
||||||
|
| 'folder-tabs-user'
|
||||||
|
| 'folder-tabs-star'
|
||||||
|
| 'folder-tabs-group'
|
||||||
|
| 'folder-tabs-folder'
|
||||||
|
| 'folder-tabs-chats'
|
||||||
|
| 'folder-tabs-chat'
|
||||||
|
| 'folder-tabs-channel'
|
||||||
|
| 'folder-tabs-bot';
|
||||||
|
|||||||
39
src/types/language.d.ts
vendored
@ -1320,6 +1320,24 @@ export interface LangPair {
|
|||||||
'GiftInfoUpgradeBadge': undefined;
|
'GiftInfoUpgradeBadge': undefined;
|
||||||
'GiftInfoUpgradeForFree': undefined;
|
'GiftInfoUpgradeForFree': undefined;
|
||||||
'GiftInfoUpgrade': undefined;
|
'GiftInfoUpgrade': undefined;
|
||||||
|
'GiftInfoCraft': undefined;
|
||||||
|
'GiftCraftTitle': undefined;
|
||||||
|
'GiftCraftNewGift': undefined;
|
||||||
|
'GiftCraftSelectTitle': undefined;
|
||||||
|
'GiftCraftSelectYourGifts': undefined;
|
||||||
|
'GiftCraftWarning': undefined;
|
||||||
|
'GiftCraftingTitle': undefined;
|
||||||
|
'GiftCraftFailedTitle': undefined;
|
||||||
|
'GiftCraftInfoTitle': undefined;
|
||||||
|
'GiftCraftInfoSubtitle': undefined;
|
||||||
|
'GiftCraftInfoCraftTitle': undefined;
|
||||||
|
'GiftCraftInfoCraftDescription': undefined;
|
||||||
|
'GiftCraftInfoChanceTitle': undefined;
|
||||||
|
'GiftCraftInfoChanceDescription': undefined;
|
||||||
|
'GiftCraftInfoRiskTitle': undefined;
|
||||||
|
'GiftCraftInfoRiskDescription': undefined;
|
||||||
|
'GiftCraftHelp': undefined;
|
||||||
|
'GiftCraftViewAll': undefined;
|
||||||
'GiftInfoWithdraw': undefined;
|
'GiftInfoWithdraw': undefined;
|
||||||
'GiftInfoWear': undefined;
|
'GiftInfoWear': undefined;
|
||||||
'GiftInfoTakeOff': undefined;
|
'GiftInfoTakeOff': undefined;
|
||||||
@ -2396,6 +2414,9 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'GiftSavedNumber': {
|
'GiftSavedNumber': {
|
||||||
'number': V;
|
'number': V;
|
||||||
};
|
};
|
||||||
|
'GiftInfoModelCrafted': {
|
||||||
|
'model': V;
|
||||||
|
};
|
||||||
'GiftInfoPeerOriginalInfo': {
|
'GiftInfoPeerOriginalInfo': {
|
||||||
'peer': V;
|
'peer': V;
|
||||||
'date': V;
|
'date': V;
|
||||||
@ -2416,6 +2437,18 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'date': V;
|
'date': V;
|
||||||
'text': V;
|
'text': V;
|
||||||
};
|
};
|
||||||
|
'GiftCraftButton': {
|
||||||
|
'giftName': V;
|
||||||
|
};
|
||||||
|
'GiftCraftSuccessChance': {
|
||||||
|
'percent': V;
|
||||||
|
};
|
||||||
|
'GiftCraftEmptyHint': {
|
||||||
|
'button': V;
|
||||||
|
};
|
||||||
|
'GiftCraftDescription': {
|
||||||
|
'giftLine': V;
|
||||||
|
};
|
||||||
'GiftTransferTONBlocked': {
|
'GiftTransferTONBlocked': {
|
||||||
'time': V;
|
'time': V;
|
||||||
};
|
};
|
||||||
@ -3665,6 +3698,12 @@ export interface LangPairPluralWithVariables<V = LangVariable> {
|
|||||||
'count': V;
|
'count': V;
|
||||||
'total': V;
|
'total': V;
|
||||||
};
|
};
|
||||||
|
'GiftCraftSelectMarketGifts': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
|
'GiftCraftFailedDescription': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
'GiftWithdrawWait': {
|
'GiftWithdrawWait': {
|
||||||
'days': V;
|
'days': V;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -31,3 +31,5 @@ export const VTT_PROFILE_BUSINESS_HOURS_COLLAPSE = VTT_PROFILE_BUSINESS_HOURS.wi
|
|||||||
export const VTT_PROFILE_NOTE = VTT_CHAT_EXTRA.with('profileNote');
|
export const VTT_PROFILE_NOTE = VTT_CHAT_EXTRA.with('profileNote');
|
||||||
export const VTT_PROFILE_NOTE_EXPAND = VTT_PROFILE_NOTE.with('profileNoteExpand');
|
export const VTT_PROFILE_NOTE_EXPAND = VTT_PROFILE_NOTE.with('profileNoteExpand');
|
||||||
export const VTT_PROFILE_NOTE_COLLAPSE = VTT_PROFILE_NOTE.with('profileNoteCollapse');
|
export const VTT_PROFILE_NOTE_COLLAPSE = VTT_PROFILE_NOTE.with('profileNoteCollapse');
|
||||||
|
|
||||||
|
export const VTT_CRAFT_ATTRIBUTES = new VTTypes(['craftAttributes']);
|
||||||
|
|||||||