Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 7 additions & 34 deletions src/lib/components/common/item_holder_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ import {
isCharm,
isHighlightCharm,
} from '../../utils/skin';
import {isSkin, floor} from '../../utils/skin';
import {isSkin} from '../../utils/skin';
import {getRankColour} from '../../utils/ranks';
import {Observe} from '../../utils/observers';
import {FetchBluegem, FetchBluegemResponse} from '../../bridge/handlers/fetch_bluegem';
import {ClientSend} from '../../bridge/client';
import {renderBluegemPercentage, renderFadePercentage, patternDetailStyles} from './pattern_details';

// Generic annotator of item holder metadata (float, seed, etc...)
// Must be extended to use as a component
export abstract class ItemHolderMetadata extends FloatElement {
static styles = [
...FloatElement.styles,
patternDetailStyles,
css`
.float {
position: absolute;
Expand All @@ -40,32 +42,10 @@ export abstract class ItemHolderMetadata extends FloatElement {
font-size: 12px;
}

.fade-base {
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}

.fade {
background-image: -webkit-linear-gradient(0deg, #d9bba5 0%, #e5903b 33%, #db5977 66%, #6775e1 100%);
}

.amber-fade {
background-image: -webkit-linear-gradient(0deg, #627d66 0%, #896944 50%, #7e4201 100%);
}

.acid-fade {
background-image: -webkit-linear-gradient(0deg, #2b441b 0%, #3e6b2f 11%, #82a64a 66%, #c1a16c 100%);
}

.csfloat-shine-fade-text {
font-weight: 1000;
-webkit-text-stroke: 1px black;
}

.bluegem {
color: deepskyblue;
}
`,
];

Expand Down Expand Up @@ -108,7 +88,7 @@ export abstract class ItemHolderMetadata extends FloatElement {
if (!this.itemInfo || !this.asset) return html``;

if (isSkin(this.asset)) {
const fadeDetails = this.asset && getFadePercentage(this.asset, this.itemInfo);
const fadeDetails = this.asset && getFadePercentage(this.asset.market_hash_name, this.itemInfo);

if (fadeDetails?.percentage === 100) {
$J(this).parent().addClass('full-fade-border');
Expand All @@ -121,17 +101,10 @@ export abstract class ItemHolderMetadata extends FloatElement {
<span class="float">${formatFloatWithRank(this.itemInfo, 6)}</span>
<span class="seed">
${formatSeed(this.itemInfo)}
${fadeDetails !== undefined
? html`<span
class="fade-base ${fadeDetails.className} ${rank && rank <= 5
? 'csfloat-shine-fade-text'
: ''}"
>(${floor(fadeDetails.percentage, 1)}%)</span
>`
: nothing}
${this.bluegemData
? html`<span class="bluegem">(${floor(this.bluegemData.playside_blue, 1)}%)</span>`
${fadeDetails
? renderFadePercentage(fadeDetails, 1, rank && rank <= 5 ? 'csfloat-shine-fade-text' : '')
: nothing}
${this.bluegemData ? renderBluegemPercentage(this.bluegemData) : nothing}
</span>
</span>
`;
Expand Down
57 changes: 57 additions & 0 deletions src/lib/components/common/pattern_details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {css, html, TemplateResult} from 'lit';

import {floor} from '../../utils/skin';
import {FetchBluegemResponse} from '../../bridge/handlers/fetch_bluegem';

export const patternDetailStyles = css`
.fade-base {
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}

.fade {
background-image: -webkit-linear-gradient(0deg, #d9bba5 0%, #e5903b 33%, #db5977 66%, #6775e1 100%);
}

.amber-fade {
background-image: -webkit-linear-gradient(0deg, #627d66 0%, #896944 50%, #7e4201 100%);
}

.acid-fade {
background-image: -webkit-linear-gradient(0deg, #2b441b 0%, #3e6b2f 11%, #82a64a 66%, #c1a16c 100%);
}

.bluegem {
color: deepskyblue;
}
`;

/**
* Renders HTML for the fade percentage.
* Requires the component to include {@link patternDetailStyles} in its static styles.
*/
export function renderFadePercentage(
fade: {percentage: number; className: string},
precision = 1,
extraClasses = ''
): TemplateResult<1> {
return html`<span class="fade-base ${fade.className} ${extraClasses}"
>(${floor(fade.percentage, precision)}%)</span
>`;
}

/**
* Renders HTML for the blue gem percentage.
* Requires the component to include {@link patternDetailStyles} in its static styles.
*/
export function renderBluegemPercentage(bluegemData: FetchBluegemResponse, showBackside = false): TemplateResult<1> {
// Some skins got only one blue value
if (showBackside && bluegemData.backside_blue !== undefined) {
return html`<span class="bluegem"
>(${floor(bluegemData.playside_blue, 1)}% / ${floor(bluegemData.backside_blue, 1)}%)</span
>`;
}

return html`<span class="bluegem">(${floor(bluegemData.playside_blue, 1)}%)</span>`;
}
5 changes: 4 additions & 1 deletion src/lib/components/inventory/selected_item_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ export class SelectedItemInfo extends FloatElement {
containerChildren.push(html`<div>Paint Seed: ${formatSeed(this.itemInfo)}</div>`);

// Fade skins
const fadePercentage = getFadePercentage(this.asset.description, this.itemInfo)?.percentage;
const fadePercentage = getFadePercentage(
this.asset.description.market_hash_name,
this.itemInfo
)?.percentage;
if (fadePercentage !== undefined) {
containerChildren.push(html`<div>Fade: ${floor(fadePercentage, 5)}%</div>`);
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/market/item_row_wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ export class ItemRowWrapper extends FloatElement {
}

if (this.itemInfo && isSkin(this.asset)) {
const fadePercentage = this.asset && getFadePercentage(this.asset, this.itemInfo)?.percentage;
const fadePercentage =
this.asset && getFadePercentage(this.asset.market_hash_name, this.itemInfo)?.percentage;

return html`
<div class="float-row-wrapper">
Expand Down
20 changes: 20 additions & 0 deletions src/lib/components/market/react/listing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {CustomElement, InjectAppend, InjectionMode} from '../../injectors';
import {isReactSteamMarket} from '../mode';
import {gFloatFetcher} from '../../../services/float_fetcher';
import {BetaListingRank} from './rank';
import {BetaListingSeedInfo} from './seed_info';

import {getFiberProps} from '../../../utils/fiber';
import type {MarketListing, MarketListingProps} from './types';
Expand All @@ -21,6 +22,7 @@ import type {MarketListing, MarketListingProps} from './types';
)
export class BetaListingEnhancer extends FloatElement {
private rankInjected = false;
private seedInfoInjected = false;

static styles = [
css`
Expand Down Expand Up @@ -70,6 +72,10 @@ export class BetaListingEnhancer extends FloatElement {
return listing.asset.assetid;
}

get marketHashName(): string | null {
return this.listing?.description.market_hash_name ?? null;
}

get targetFloat(): number | null {
const wearProp = this.listing?.asset.asset_properties?.find((p) => p.propertyid === 2);
// this is a number in the React properties, but a string in the rgAsset properties
Expand Down Expand Up @@ -105,6 +111,7 @@ export class BetaListingEnhancer extends FloatElement {
}

this.injectRank(info);
this.injectSeedInfo(info);
}

private injectRank(info: ItemInfo): void {
Expand All @@ -118,4 +125,17 @@ export class BetaListingEnhancer extends FloatElement {
// Append into the card; the element repositions itself correctly.
this.card.appendChild(rank);
}

private injectSeedInfo(info: ItemInfo): void {
if (this.seedInfoInjected) return;
this.seedInfoInjected = true;

const seedInfo = BetaListingSeedInfo.elem() as BetaListingSeedInfo;
seedInfo.itemInfo = info;
seedInfo.card = this.card;
seedInfo.targetPaintSeed = info.paintseed;
seedInfo.marketHashName = this.marketHashName ?? '';
// Append into the card; the element repositions itself correctly.
this.card.appendChild(seedInfo);
}
}
107 changes: 107 additions & 0 deletions src/lib/components/market/react/seed_info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import {css, html, nothing} from 'lit';
import {property, state} from 'lit/decorators.js';

import {CustomElement} from '../../injectors';
import {FloatElement} from '../../custom';
import {ItemInfo} from '../../../bridge/handlers/fetch_inspect_info';
import {getFadePercentage, isBlueSkin} from '../../../utils/skin';
import {getDopplerPhase, hasDopplerPhase} from '../../../utils/dopplers';
import {ClientSend} from '../../../bridge/client';
import {FetchBluegem, FetchBluegemResponse} from '../../../bridge/handlers/fetch_bluegem';
import {renderBluegemPercentage, renderFadePercentage, patternDetailStyles} from '../../common/pattern_details';

/**
* Renders the fade percentage and blue-gem percentage next to the paint seed in the Steam Market beta,
* mirroring {@link BetaListingRank}. Self-contained: fetches its own blue-gem data.
*/
@CustomElement()
export class BetaListingSeedInfo extends FloatElement {
@property({type: Object}) itemInfo!: ItemInfo;
@property({attribute: false}) card!: HTMLElement;
@property({attribute: false}) targetPaintSeed!: number | null;
@property({attribute: false}) marketHashName!: string;

@state() private bluegemData: FetchBluegemResponse | undefined;

static styles = [
patternDetailStyles,
css`
:host {
margin-left: 4px;
}
`,
];

private injected = false;

connectedCallback(): void {
super.connectedCallback();
void this.init();
}

private async init(): Promise<void> {
if (isBlueSkin(this.itemInfo)) {
try {
this.bluegemData = await ClientSend(FetchBluegem, {iteminfo: this.itemInfo});
} catch (e) {
this.bluegemData = undefined;
}
}

this.placeNextToSeed();
}

private get fadeDetails(): {percentage: number; className: string} | undefined {
return this.marketHashName ? getFadePercentage(this.marketHashName, this.itemInfo) : undefined;
}

private get dopplerPhase(): string | undefined {
return this.itemInfo && hasDopplerPhase(this.itemInfo.paintindex)
? getDopplerPhase(this.itemInfo.paintindex)
: undefined;
}

private placeNextToSeed(): void {
if (this.injected || !this.itemInfo || !this.card) return;
if (this.fadeDetails === undefined && !this.bluegemData && !this.dopplerPhase) return;

const seedSpan = this.findSeedSpan();
if (!seedSpan) return;

this.injected = true;
seedSpan.insertAdjacentElement('afterend', this);
}

private findSeedSpan(): HTMLSpanElement | null {
if (this.targetPaintSeed === null) return null;

const spans = this.card.querySelectorAll<HTMLSpanElement>('span[style*="pre-wrap"]');
for (const span of spans) {
const text = span.textContent?.trim();
if (!text) continue;
const value = parseInt(text, 10);
// String(value) === text avoids matching the decimal float span.
if (!Number.isNaN(value) && String(value) === text && value === this.targetPaintSeed) return span;
}
return null;
}

protected render() {
if (!this.itemInfo || !this.card) return nothing;

if (this.fadeDetails) {
return renderFadePercentage(this.fadeDetails, 2);
}

if (this.bluegemData) {
return renderBluegemPercentage(this.bluegemData, true);
}

const phase = this.dopplerPhase;
if (phase) {
return html`<span>(${phase})</span>`;
}

return nothing;
}
}
4 changes: 2 additions & 2 deletions src/lib/components/market/sort_listings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class SortListings extends FloatElement {

const asset = g_rgAssets[AppId.CSGO][ContextId.PRIMARY][listingInfo.asset.id];

return getFadeParams(asset) !== undefined;
return getFadeParams(asset.market_hash_name) !== undefined;
}

connectedCallback() {
Expand Down Expand Up @@ -130,7 +130,7 @@ export class SortListings extends FloatElement {
info,
listingId: listingId!,
converted_price: listingInfo?.converted_price || 0,
fadePercentage: (asset && getFadePercentage(asset, info)?.percentage) || 0,
fadePercentage: (asset && getFadePercentage(asset.market_hash_name, info)?.percentage) || 0,
};
} catch (error) {
console.error(`CSFloat: Failed to fetch float for listing ${listingId}:`, error);
Expand Down
8 changes: 4 additions & 4 deletions src/lib/utils/skin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export function isBlueSkin(itemInfo: ItemInfo): boolean {
);
}

export function getFadeParams(asset: rgAsset):
export function getFadeParams(marketHashName: string):
| {
calculator: typeof FadeCalculator | typeof AcidFadeCalculator | typeof AmberFadeCalculator;
weaponName: string;
Expand All @@ -198,7 +198,7 @@ export function getFadeParams(asset: rgAsset):

for (const [fadeType, calculator] of Object.entries(FADE_TYPE_TO_CALCULATOR)) {
for (const supportedWeapon of calculator.getSupportedWeapons()) {
if (asset.market_hash_name.includes(`${supportedWeapon} | ${fadeType}`)) {
if (marketHashName.includes(`${supportedWeapon} | ${fadeType}`)) {
return {
calculator,
weaponName: supportedWeapon.toString(),
Expand All @@ -210,10 +210,10 @@ export function getFadeParams(asset: rgAsset):
}

export function getFadePercentage(
asset: rgAsset,
marketHashName: string,
itemInfo: ItemInfo
): {percentage: number; className: string} | undefined {
const fadeInfo = getFadeParams(asset);
const fadeInfo = getFadeParams(marketHashName);

if (fadeInfo !== undefined) {
const {calculator, weaponName, className} = fadeInfo;
Expand Down