@rpg-engine/long-bow 0.8.137 → 0.8.138
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Marketplace/BuyPanel.d.ts +4 -1
- package/dist/components/Marketplace/Marketplace.d.ts +4 -1
- package/dist/components/Marketplace/MarketplaceBuyModal.d.ts +10 -0
- package/dist/components/Marketplace/MarketplaceRows.d.ts +1 -0
- package/dist/components/Store/sections/StorePacksSection.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +283 -74
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +283 -75
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DCWallet/DCHistoryPanel.tsx +15 -3
- package/src/components/Marketplace/BuyPanel.tsx +29 -2
- package/src/components/Marketplace/Marketplace.tsx +4 -1
- package/src/components/Marketplace/MarketplaceBuyModal.tsx +230 -0
- package/src/components/Marketplace/MarketplaceRows.tsx +41 -16
- package/src/components/Store/Store.tsx +5 -1
- package/src/components/Store/sections/StorePacksSection.tsx +17 -1
- package/src/index.tsx +1 -0
package/package.json
CHANGED
|
@@ -24,6 +24,17 @@ const TRANSACTION_TYPE_LABELS: Record<string, string> = {
|
|
|
24
24
|
AdminAdjustment: 'Admin',
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
const TRANSACTION_TYPE_COLORS: Record<string, string> = {
|
|
28
|
+
Purchase: '#22c55e', // green — DC coming in
|
|
29
|
+
Transfer: '#60a5fa', // blue — P2P transfer
|
|
30
|
+
MarketplaceSale: '#fbbf24', // gold — sold item
|
|
31
|
+
MarketplacePurchase: '#f97316', // orange — bought item
|
|
32
|
+
StorePurchase: '#a78bfa', // purple — store
|
|
33
|
+
Fee: '#ef4444', // red — cost
|
|
34
|
+
Refund: '#06b6d4', // cyan — money back
|
|
35
|
+
AdminAdjustment: '#9ca3af', // gray — admin
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
const TRANSACTION_TYPE_OPTIONS = [
|
|
28
39
|
{ value: '', label: 'All Types' },
|
|
29
40
|
{ value: 'Purchase', label: 'Purchase' },
|
|
@@ -101,12 +112,13 @@ export const DCHistoryPanel: React.FC<IDCHistoryPanelProps> = ({
|
|
|
101
112
|
{transactions.map((tx) => {
|
|
102
113
|
const isCredit = tx.amount > 0;
|
|
103
114
|
const label = TRANSACTION_TYPE_LABELS[tx.type] ?? tx.type;
|
|
115
|
+
const color = TRANSACTION_TYPE_COLORS[tx.type] ?? '#f59e0b';
|
|
104
116
|
const subtitle = tx.note ?? (tx.relatedCharacterName ? tx.relatedCharacterName : '');
|
|
105
117
|
|
|
106
118
|
return (
|
|
107
119
|
<TransactionRow key={tx._id}>
|
|
108
120
|
<TxLeft>
|
|
109
|
-
<TxType>{label}</TxType>
|
|
121
|
+
<TxType $color={color}>{label}</TxType>
|
|
110
122
|
{subtitle ? <TxNote>{subtitle}</TxNote> : null}
|
|
111
123
|
</TxLeft>
|
|
112
124
|
<TxRight>
|
|
@@ -225,9 +237,9 @@ const TxRight = styled.div`
|
|
|
225
237
|
flex-shrink: 0;
|
|
226
238
|
`;
|
|
227
239
|
|
|
228
|
-
const TxType = styled.span
|
|
240
|
+
const TxType = styled.span<{ $color: string }>`
|
|
229
241
|
font-size: 7px;
|
|
230
|
-
color:
|
|
242
|
+
color: ${({ $color }) => $color};
|
|
231
243
|
font-family: 'Press Start 2P', cursive;
|
|
232
244
|
`;
|
|
233
245
|
|
|
@@ -5,6 +5,7 @@ import styled from 'styled-components';
|
|
|
5
5
|
import { ConfirmModal } from '../ConfirmModal';
|
|
6
6
|
import { Dropdown } from '../Dropdown';
|
|
7
7
|
import { Input } from '../Input';
|
|
8
|
+
import { MarketplaceBuyModal, MarketplacePaymentMethod } from './MarketplaceBuyModal';
|
|
8
9
|
import { MarketplaceRows } from './MarketplaceRows';
|
|
9
10
|
import { itemRarityOptions, itemTypeOptions, orderByOptions } from './filters';
|
|
10
11
|
|
|
@@ -26,11 +27,13 @@ export interface IBuyPanelProps {
|
|
|
26
27
|
onChangePriceInput: (value: [number | undefined, number | undefined]) => void;
|
|
27
28
|
scale?: number;
|
|
28
29
|
equipmentSet?: IEquipmentSet | null;
|
|
29
|
-
onMarketPlaceItemBuy?: (marketPlaceItemId: string) => void;
|
|
30
|
+
onMarketPlaceItemBuy?: (marketPlaceItemId: string, paymentMethod?: MarketplacePaymentMethod) => void;
|
|
30
31
|
characterId: string;
|
|
31
32
|
enableHotkeys?: () => void;
|
|
32
33
|
disableHotkeys?: () => void;
|
|
33
34
|
currentPage: number;
|
|
35
|
+
dcBalance?: number;
|
|
36
|
+
dcToGoldSwapRate?: number;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
@@ -50,6 +53,8 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
50
53
|
enableHotkeys,
|
|
51
54
|
disableHotkeys,
|
|
52
55
|
currentPage,
|
|
56
|
+
dcBalance = 0,
|
|
57
|
+
dcToGoldSwapRate = 0,
|
|
53
58
|
}) => {
|
|
54
59
|
const [name, setName] = useState('');
|
|
55
60
|
const [mainLevel, setMainLevel] = useState<
|
|
@@ -70,9 +75,30 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
70
75
|
itemsContainer.current?.scrollTo(0, 0);
|
|
71
76
|
}, [currentPage]);
|
|
72
77
|
|
|
78
|
+
const buyingItem = buyingItemId
|
|
79
|
+
? items.find(i => i._id === buyingItemId)
|
|
80
|
+
: null;
|
|
81
|
+
const hasDCBalance = dcBalance > 0 && dcToGoldSwapRate > 0;
|
|
82
|
+
|
|
83
|
+
const getDCEquivalentPrice = (goldPrice: number): number =>
|
|
84
|
+
dcToGoldSwapRate > 0 ? Math.ceil(goldPrice / dcToGoldSwapRate) : 0;
|
|
85
|
+
|
|
73
86
|
return (
|
|
74
87
|
<>
|
|
75
|
-
{buyingItemId && (
|
|
88
|
+
{buyingItemId && buyingItem && hasDCBalance && (
|
|
89
|
+
<MarketplaceBuyModal
|
|
90
|
+
goldPrice={buyingItem.price}
|
|
91
|
+
dcEquivalentPrice={getDCEquivalentPrice(buyingItem.price)}
|
|
92
|
+
dcBalance={dcBalance}
|
|
93
|
+
onClose={() => setBuyingItemId(null)}
|
|
94
|
+
onConfirm={(paymentMethod) => {
|
|
95
|
+
onMarketPlaceItemBuy?.(buyingItemId, paymentMethod);
|
|
96
|
+
setBuyingItemId(null);
|
|
97
|
+
enableHotkeys?.();
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
{buyingItemId && !hasDCBalance && (
|
|
76
102
|
<ConfirmModal
|
|
77
103
|
onClose={setBuyingItemId.bind(null, null)}
|
|
78
104
|
onConfirm={() => {
|
|
@@ -216,6 +242,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
216
242
|
atlasJSON={atlasJSON}
|
|
217
243
|
item={item}
|
|
218
244
|
itemPrice={price}
|
|
245
|
+
dcEquivalentPrice={dcToGoldSwapRate > 0 ? getDCEquivalentPrice(price) : undefined}
|
|
219
246
|
equipmentSet={equipmentSet}
|
|
220
247
|
onMarketPlaceItemBuy={setBuyingItemId.bind(null, _id)}
|
|
221
248
|
disabled={owner === characterId}
|
|
@@ -5,6 +5,7 @@ import { Button, ButtonTypes } from '../Button';
|
|
|
5
5
|
import { DraggableContainer } from '../DraggableContainer';
|
|
6
6
|
import { Pager } from '../Pager';
|
|
7
7
|
import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
|
|
8
|
+
import { MarketplacePaymentMethod } from './MarketplaceBuyModal';
|
|
8
9
|
import { BuyPanel } from './BuyPanel';
|
|
9
10
|
import { ManagmentPanel } from './ManagmentPanel';
|
|
10
11
|
|
|
@@ -26,7 +27,7 @@ export interface IMarketPlaceProps {
|
|
|
26
27
|
onChangePriceInput: (value: [number | undefined, number | undefined]) => void;
|
|
27
28
|
scale?: number;
|
|
28
29
|
equipmentSet?: IEquipmentSet | null;
|
|
29
|
-
onMarketPlaceItemBuy?: (marketPlaceItemId: string) => void;
|
|
30
|
+
onMarketPlaceItemBuy?: (marketPlaceItemId: string, paymentMethod?: MarketplacePaymentMethod) => void;
|
|
30
31
|
onMarketPlaceItemRemove?: (marketPlaceItemId: string) => void;
|
|
31
32
|
availableGold: number;
|
|
32
33
|
selectedItemToSell: IItem | null;
|
|
@@ -41,6 +42,8 @@ export interface IMarketPlaceProps {
|
|
|
41
42
|
currentPage: number;
|
|
42
43
|
itemsPerPage: number;
|
|
43
44
|
onPageChange: (page: number) => void;
|
|
45
|
+
dcBalance?: number;
|
|
46
|
+
dcToGoldSwapRate?: number;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export const Marketplace: React.FC<IMarketPlaceProps> = props => {
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { FaTimes } from 'react-icons/fa';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import ModalPortal from '../Abstractions/ModalPortal';
|
|
5
|
+
import { Button, ButtonTypes } from '../Button';
|
|
6
|
+
|
|
7
|
+
export type MarketplacePaymentMethod = 'gold' | 'dc';
|
|
8
|
+
|
|
9
|
+
export interface IMarketplaceBuyModalProps {
|
|
10
|
+
goldPrice: number;
|
|
11
|
+
dcEquivalentPrice: number;
|
|
12
|
+
dcBalance: number;
|
|
13
|
+
onConfirm: (paymentMethod: MarketplacePaymentMethod) => void;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const MarketplaceBuyModal: React.FC<IMarketplaceBuyModalProps> = ({
|
|
18
|
+
goldPrice,
|
|
19
|
+
dcEquivalentPrice,
|
|
20
|
+
dcBalance,
|
|
21
|
+
onConfirm,
|
|
22
|
+
onClose,
|
|
23
|
+
}) => {
|
|
24
|
+
const [selected, setSelected] = useState<MarketplacePaymentMethod>('gold');
|
|
25
|
+
const hasSufficientDC = dcBalance >= dcEquivalentPrice;
|
|
26
|
+
|
|
27
|
+
const stopPropagation = useCallback(
|
|
28
|
+
(e: React.MouseEvent | React.TouchEvent | React.PointerEvent) => {
|
|
29
|
+
e.stopPropagation();
|
|
30
|
+
},
|
|
31
|
+
[]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const handleConfirm = useCallback(() => {
|
|
35
|
+
onConfirm(selected);
|
|
36
|
+
}, [selected, onConfirm]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<ModalPortal>
|
|
40
|
+
<Overlay onPointerDown={onClose} />
|
|
41
|
+
<ModalContainer>
|
|
42
|
+
<ModalContent
|
|
43
|
+
onClick={stopPropagation as React.MouseEventHandler}
|
|
44
|
+
onTouchStart={stopPropagation as React.TouchEventHandler}
|
|
45
|
+
onPointerDown={stopPropagation as React.PointerEventHandler}
|
|
46
|
+
>
|
|
47
|
+
<Header>
|
|
48
|
+
<Title>Confirm Purchase</Title>
|
|
49
|
+
<CloseButton onPointerDown={onClose} aria-label="Close">
|
|
50
|
+
<FaTimes />
|
|
51
|
+
</CloseButton>
|
|
52
|
+
</Header>
|
|
53
|
+
|
|
54
|
+
<Options>
|
|
55
|
+
<RadioOption
|
|
56
|
+
$selected={selected === 'gold'}
|
|
57
|
+
onPointerDown={() => setSelected('gold')}
|
|
58
|
+
>
|
|
59
|
+
<RadioCircle $selected={selected === 'gold'} />
|
|
60
|
+
<OptionText>
|
|
61
|
+
<OptionLabel>Gold</OptionLabel>
|
|
62
|
+
<OptionSub>{goldPrice.toLocaleString()} gold</OptionSub>
|
|
63
|
+
</OptionText>
|
|
64
|
+
</RadioOption>
|
|
65
|
+
|
|
66
|
+
<RadioOption
|
|
67
|
+
$selected={selected === 'dc'}
|
|
68
|
+
$disabled={!hasSufficientDC}
|
|
69
|
+
onPointerDown={() => hasSufficientDC && setSelected('dc')}
|
|
70
|
+
>
|
|
71
|
+
<RadioCircle $selected={selected === 'dc'} />
|
|
72
|
+
<OptionText>
|
|
73
|
+
<OptionLabel $disabled={!hasSufficientDC}>Definya Coin</OptionLabel>
|
|
74
|
+
<OptionSub>
|
|
75
|
+
{dcEquivalentPrice.toLocaleString()} DC
|
|
76
|
+
{' '}
|
|
77
|
+
<BalanceHint $insufficient={!hasSufficientDC}>
|
|
78
|
+
({dcBalance.toLocaleString()} available)
|
|
79
|
+
</BalanceHint>
|
|
80
|
+
</OptionSub>
|
|
81
|
+
</OptionText>
|
|
82
|
+
</RadioOption>
|
|
83
|
+
</Options>
|
|
84
|
+
|
|
85
|
+
<ConfirmRow>
|
|
86
|
+
<Button buttonType={ButtonTypes.RPGUIButton} onPointerDown={handleConfirm}>
|
|
87
|
+
Confirm
|
|
88
|
+
</Button>
|
|
89
|
+
</ConfirmRow>
|
|
90
|
+
</ModalContent>
|
|
91
|
+
</ModalContainer>
|
|
92
|
+
</ModalPortal>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const Overlay = styled.div`
|
|
97
|
+
position: fixed;
|
|
98
|
+
inset: 0;
|
|
99
|
+
background: rgba(0, 0, 0, 0.65);
|
|
100
|
+
z-index: 1000;
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const ModalContainer = styled.div`
|
|
104
|
+
position: fixed;
|
|
105
|
+
inset: 0;
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
justify-content: center;
|
|
109
|
+
z-index: 1001;
|
|
110
|
+
pointer-events: none;
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
const ModalContent = styled.div`
|
|
114
|
+
background: #1a1a2e;
|
|
115
|
+
border: 2px solid #f59e0b;
|
|
116
|
+
border-radius: 8px;
|
|
117
|
+
padding: 20px 24px 24px;
|
|
118
|
+
min-width: 300px;
|
|
119
|
+
max-width: 90%;
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
gap: 16px;
|
|
123
|
+
pointer-events: auto;
|
|
124
|
+
animation: scaleIn 0.15s ease-out;
|
|
125
|
+
|
|
126
|
+
@keyframes scaleIn {
|
|
127
|
+
from { transform: scale(0.85); opacity: 0; }
|
|
128
|
+
to { transform: scale(1); opacity: 1; }
|
|
129
|
+
}
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
const Header = styled.div`
|
|
133
|
+
display: flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
const Title = styled.h3`
|
|
139
|
+
margin: 0;
|
|
140
|
+
font-family: 'Press Start 2P', cursive;
|
|
141
|
+
font-size: 0.7rem;
|
|
142
|
+
color: #fef08a;
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
const CloseButton = styled.button`
|
|
146
|
+
background: none;
|
|
147
|
+
border: none;
|
|
148
|
+
color: rgba(255, 255, 255, 0.6);
|
|
149
|
+
cursor: pointer;
|
|
150
|
+
font-size: 1rem;
|
|
151
|
+
padding: 4px;
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
|
|
155
|
+
&:hover {
|
|
156
|
+
color: #ffffff;
|
|
157
|
+
}
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
const Options = styled.div`
|
|
161
|
+
display: flex;
|
|
162
|
+
flex-direction: column;
|
|
163
|
+
gap: 8px;
|
|
164
|
+
`;
|
|
165
|
+
|
|
166
|
+
const RadioOption = styled.div<{ $selected: boolean; $disabled?: boolean }>`
|
|
167
|
+
display: flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
gap: 12px;
|
|
170
|
+
padding: 10px 12px;
|
|
171
|
+
border: 1px solid ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(255,255,255,0.15)')};
|
|
172
|
+
border-radius: 6px;
|
|
173
|
+
background: ${({ $selected }) => ($selected ? 'rgba(245,158,11,0.1)' : 'transparent')};
|
|
174
|
+
cursor: ${({ $disabled }) => ($disabled ? 'not-allowed' : 'pointer')};
|
|
175
|
+
opacity: ${({ $disabled }) => ($disabled ? 0.5 : 1)};
|
|
176
|
+
transition: border-color 0.15s, background 0.15s;
|
|
177
|
+
|
|
178
|
+
&:hover {
|
|
179
|
+
border-color: ${({ $disabled }) => ($disabled ? 'rgba(255,255,255,0.15)' : '#f59e0b')};
|
|
180
|
+
}
|
|
181
|
+
`;
|
|
182
|
+
|
|
183
|
+
const RadioCircle = styled.div<{ $selected: boolean }>`
|
|
184
|
+
width: 16px;
|
|
185
|
+
height: 16px;
|
|
186
|
+
border-radius: 50%;
|
|
187
|
+
border: 2px solid ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(255,255,255,0.4)')};
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
justify-content: center;
|
|
191
|
+
flex-shrink: 0;
|
|
192
|
+
|
|
193
|
+
&::after {
|
|
194
|
+
content: '';
|
|
195
|
+
width: 8px;
|
|
196
|
+
height: 8px;
|
|
197
|
+
border-radius: 50%;
|
|
198
|
+
background: #f59e0b;
|
|
199
|
+
opacity: ${({ $selected }) => ($selected ? 1 : 0)};
|
|
200
|
+
transition: opacity 0.15s;
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
|
|
204
|
+
const OptionText = styled.div`
|
|
205
|
+
display: flex;
|
|
206
|
+
flex-direction: column;
|
|
207
|
+
gap: 3px;
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
const OptionLabel = styled.span<{ $disabled?: boolean }>`
|
|
211
|
+
font-family: 'Press Start 2P', cursive;
|
|
212
|
+
font-size: 0.65rem;
|
|
213
|
+
color: ${({ $disabled }) => ($disabled ? 'rgba(255,255,255,0.4)' : '#ffffff')};
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
const OptionSub = styled.span`
|
|
217
|
+
font-family: 'Press Start 2P', cursive;
|
|
218
|
+
font-size: 0.5rem;
|
|
219
|
+
color: rgba(255, 255, 255, 0.55);
|
|
220
|
+
`;
|
|
221
|
+
|
|
222
|
+
const BalanceHint = styled.span<{ $insufficient: boolean }>`
|
|
223
|
+
color: ${({ $insufficient }) => ($insufficient ? '#ef4444' : 'rgba(255, 255, 255, 0.4)')};
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
const ConfirmRow = styled.div`
|
|
227
|
+
display: flex;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
margin-top: 4px;
|
|
230
|
+
`;
|
|
@@ -19,6 +19,7 @@ export interface IMarketPlaceRowsPropos {
|
|
|
19
19
|
atlasIMG: any;
|
|
20
20
|
item: IItem;
|
|
21
21
|
itemPrice: number;
|
|
22
|
+
dcEquivalentPrice?: number;
|
|
22
23
|
equipmentSet?: IEquipmentSet | null;
|
|
23
24
|
scale?: number;
|
|
24
25
|
onMarketPlaceItemBuy?: () => void;
|
|
@@ -31,6 +32,7 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
31
32
|
atlasIMG,
|
|
32
33
|
item,
|
|
33
34
|
itemPrice,
|
|
35
|
+
dcEquivalentPrice,
|
|
34
36
|
equipmentSet,
|
|
35
37
|
scale,
|
|
36
38
|
onMarketPlaceItemBuy,
|
|
@@ -88,23 +90,32 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
88
90
|
</ItemIconContainer>
|
|
89
91
|
|
|
90
92
|
<Flex>
|
|
91
|
-
<
|
|
92
|
-
<
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
|
|
93
|
+
<PriceContainer>
|
|
94
|
+
<ItemIconContainer>
|
|
95
|
+
<GoldContainer>
|
|
96
|
+
<SpriteFromAtlas
|
|
97
|
+
atlasIMG={atlasIMG}
|
|
98
|
+
atlasJSON={atlasJSON}
|
|
99
|
+
spriteKey="others/gold-coin-qty-5.png"
|
|
100
|
+
imgScale={2}
|
|
101
|
+
/>
|
|
102
|
+
</GoldContainer>
|
|
103
|
+
<PriceValue>
|
|
104
|
+
<p>
|
|
105
|
+
<Ellipsis maxLines={1} maxWidth="120px" fontSize="10px">
|
|
106
|
+
${itemPrice}
|
|
107
|
+
</Ellipsis>
|
|
108
|
+
</p>
|
|
109
|
+
</PriceValue>
|
|
110
|
+
</ItemIconContainer>
|
|
111
|
+
{dcEquivalentPrice !== undefined && (
|
|
112
|
+
<DCPriceLabel>
|
|
113
|
+
<Ellipsis maxLines={1} maxWidth="80px" fontSize="9px">
|
|
114
|
+
{dcEquivalentPrice} DC
|
|
104
115
|
</Ellipsis>
|
|
105
|
-
</
|
|
106
|
-
|
|
107
|
-
</
|
|
116
|
+
</DCPriceLabel>
|
|
117
|
+
)}
|
|
118
|
+
</PriceContainer>
|
|
108
119
|
<ButtonContainer>
|
|
109
120
|
<Button
|
|
110
121
|
buttonType={ButtonTypes.RPGUIButton}
|
|
@@ -176,6 +187,20 @@ const SpriteContainer = styled.div`
|
|
|
176
187
|
left: 0.5rem;
|
|
177
188
|
`;
|
|
178
189
|
|
|
190
|
+
const PriceContainer = styled.div`
|
|
191
|
+
display: flex;
|
|
192
|
+
flex-direction: column;
|
|
193
|
+
align-items: flex-start;
|
|
194
|
+
gap: 2px;
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const DCPriceLabel = styled.p`
|
|
198
|
+
margin: 0;
|
|
199
|
+
margin-left: 40px;
|
|
200
|
+
color: #fef08a;
|
|
201
|
+
font-size: 0.7rem;
|
|
202
|
+
`;
|
|
203
|
+
|
|
179
204
|
const PriceValue = styled.div`
|
|
180
205
|
margin-left: 40px;
|
|
181
206
|
`;
|
|
@@ -168,6 +168,8 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
168
168
|
packs={packs.filter(pack => pack.priceUSD >= 9.99)}
|
|
169
169
|
onAddToCart={handleAddPackToCart}
|
|
170
170
|
onSelectPack={setSelectedPack}
|
|
171
|
+
atlasJSON={atlasJSON}
|
|
172
|
+
atlasIMG={atlasIMG}
|
|
171
173
|
/>
|
|
172
174
|
),
|
|
173
175
|
},
|
|
@@ -176,9 +178,11 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
176
178
|
title: packsTabLabel,
|
|
177
179
|
content: customPacksContent ?? (
|
|
178
180
|
<StorePacksSection
|
|
179
|
-
packs={packs.filter(pack => pack.priceUSD < 9.99)}
|
|
181
|
+
packs={hidePremiumTab ? packs : packs.filter(pack => pack.priceUSD < 9.99)}
|
|
180
182
|
onAddToCart={handleAddPackToCart}
|
|
181
183
|
onSelectPack={setSelectedPack}
|
|
184
|
+
atlasJSON={atlasJSON}
|
|
185
|
+
atlasIMG={atlasIMG}
|
|
182
186
|
/>
|
|
183
187
|
),
|
|
184
188
|
},
|
|
@@ -3,6 +3,7 @@ import React, { useCallback } from 'react';
|
|
|
3
3
|
import { FaCartPlus } from 'react-icons/fa';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import { CTAButton } from '../../shared/CTAButton/CTAButton';
|
|
6
|
+
import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
|
|
6
7
|
import { ScrollableContent } from '../../shared/ScrollableContent/ScrollableContent';
|
|
7
8
|
import { usePackFiltering } from '../../../hooks/usePackFiltering';
|
|
8
9
|
|
|
@@ -10,12 +11,16 @@ interface IStorePacksSectionProps {
|
|
|
10
11
|
packs: IItemPack[];
|
|
11
12
|
onAddToCart: (pack: IItemPack) => void;
|
|
12
13
|
onSelectPack?: (pack: IItemPack) => void;
|
|
14
|
+
atlasJSON?: any;
|
|
15
|
+
atlasIMG?: string;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export const StorePacksSection: React.FC<IStorePacksSectionProps> = ({
|
|
16
19
|
packs,
|
|
17
20
|
onAddToCart,
|
|
18
21
|
onSelectPack,
|
|
22
|
+
atlasJSON,
|
|
23
|
+
atlasIMG,
|
|
19
24
|
}) => {
|
|
20
25
|
const { searchQuery, setSearchQuery, filteredPacks } = usePackFiltering(packs);
|
|
21
26
|
|
|
@@ -24,11 +29,22 @@ export const StorePacksSection: React.FC<IStorePacksSectionProps> = ({
|
|
|
24
29
|
return imageUrl.default || imageUrl.src;
|
|
25
30
|
};
|
|
26
31
|
|
|
32
|
+
const renderPackIcon = useCallback(
|
|
33
|
+
(pack: IItemPack) => {
|
|
34
|
+
const imgSrc = getImageSrc(pack.image);
|
|
35
|
+
if (atlasJSON && atlasIMG && imgSrc && atlasJSON?.frames?.[imgSrc]) {
|
|
36
|
+
return <SpriteFromAtlas atlasJSON={atlasJSON} atlasIMG={atlasIMG} spriteKey={imgSrc} width={40} height={40} imgScale={1.2} centered />;
|
|
37
|
+
}
|
|
38
|
+
return <img src={imgSrc} alt={pack.title} />;
|
|
39
|
+
},
|
|
40
|
+
[atlasJSON, atlasIMG]
|
|
41
|
+
);
|
|
42
|
+
|
|
27
43
|
const renderPack = useCallback(
|
|
28
44
|
(pack: IItemPack) => (
|
|
29
45
|
<PackRow key={pack.key} onClick={() => onSelectPack?.(pack)}>
|
|
30
46
|
<PackIconContainer>
|
|
31
|
-
|
|
47
|
+
{renderPackIcon(pack)}
|
|
32
48
|
</PackIconContainer>
|
|
33
49
|
|
|
34
50
|
<PackDetails>
|
package/src/index.tsx
CHANGED
|
@@ -35,6 +35,7 @@ export * from './components/itemSelector/ItemSelector';
|
|
|
35
35
|
export * from './components/Leaderboard/Leaderboard';
|
|
36
36
|
export * from './components/ListMenu';
|
|
37
37
|
export * from './components/Marketplace/Marketplace';
|
|
38
|
+
export * from './components/Marketplace/MarketplaceBuyModal';
|
|
38
39
|
export * from './components/Marketplace/MarketplaceRows';
|
|
39
40
|
export * from './components/Multitab/TabBody';
|
|
40
41
|
export * from './components/Multitab/TabsContainer';
|