@rpg-engine/long-bow 0.8.137 → 0.8.139
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 +280 -79
- 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 +281 -81
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DCWallet/DCHistoryPanel.tsx +16 -6
- package/src/components/DCWallet/DCWalletModal.tsx +1 -1
- package/src/components/Marketplace/BuyPanel.tsx +30 -3
- package/src/components/Marketplace/Marketplace.tsx +4 -1
- package/src/components/Marketplace/MarketplaceBuyModal.tsx +231 -0
- package/src/components/Marketplace/MarketplaceRows.tsx +14 -1
- package/src/components/Store/Store.tsx +5 -2
- 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,16 +112,17 @@ 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
|
-
<TransactionRow key={tx._id}>
|
|
119
|
+
<TransactionRow key={tx._id} style={{ borderLeft: `3px solid ${color}` }}>
|
|
108
120
|
<TxLeft>
|
|
109
|
-
<TxType>{label}</TxType>
|
|
121
|
+
<TxType style={{ color }}>{label}</TxType>
|
|
110
122
|
{subtitle ? <TxNote>{subtitle}</TxNote> : null}
|
|
111
123
|
</TxLeft>
|
|
112
124
|
<TxRight>
|
|
113
|
-
<TxAmount
|
|
125
|
+
<TxAmount style={{ color: isCredit ? '#4CAF50' : '#D04648' }}>
|
|
114
126
|
{isCredit ? '+' : ''}{tx.amount} DC
|
|
115
127
|
</TxAmount>
|
|
116
128
|
<TxDate>{formatDate(tx.createdAt)}</TxDate>
|
|
@@ -227,14 +239,12 @@ const TxRight = styled.div`
|
|
|
227
239
|
|
|
228
240
|
const TxType = styled.span`
|
|
229
241
|
font-size: 7px;
|
|
230
|
-
color: #f59e0b;
|
|
231
242
|
font-family: 'Press Start 2P', cursive;
|
|
232
243
|
`;
|
|
233
244
|
|
|
234
|
-
const TxAmount = styled.span
|
|
245
|
+
const TxAmount = styled.span`
|
|
235
246
|
font-size: 8px;
|
|
236
247
|
font-family: 'Press Start 2P', cursive;
|
|
237
|
-
color: ${({ $credit }) => ($credit ? uiColors.green : uiColors.red)};
|
|
238
248
|
white-space: nowrap;
|
|
239
249
|
`;
|
|
240
250
|
|
|
@@ -64,7 +64,7 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
|
|
|
64
64
|
{onBuyDC && (
|
|
65
65
|
<BuyButton onPointerDown={onBuyDC} title="Buy Definya Coins">
|
|
66
66
|
<FaShoppingCart />
|
|
67
|
-
<BuyButtonLabel>Buy DC</BuyButtonLabel>
|
|
67
|
+
<BuyButtonLabel>Buy More DC</BuyButtonLabel>
|
|
68
68
|
</BuyButton>
|
|
69
69
|
)}
|
|
70
70
|
</BalanceContent>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { IEquipmentSet, IMarketplaceItem } from '@rpg-engine/shared';
|
|
1
|
+
import { goldToDC, IEquipmentSet, IMarketplaceItem } from '@rpg-engine/shared';
|
|
2
2
|
import React, { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { AiFillCaretRight } from 'react-icons/ai';
|
|
4
4
|
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 ? goldToDC(goldPrice) : 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,231 @@
|
|
|
1
|
+
import { formatDCAmount } from '@rpg-engine/shared';
|
|
2
|
+
import React, { useCallback, useState } from 'react';
|
|
3
|
+
import { FaTimes } from 'react-icons/fa';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import ModalPortal from '../Abstractions/ModalPortal';
|
|
6
|
+
import { Button, ButtonTypes } from '../Button';
|
|
7
|
+
|
|
8
|
+
export type MarketplacePaymentMethod = 'gold' | 'dc';
|
|
9
|
+
|
|
10
|
+
export interface IMarketplaceBuyModalProps {
|
|
11
|
+
goldPrice: number;
|
|
12
|
+
dcEquivalentPrice: number;
|
|
13
|
+
dcBalance: number;
|
|
14
|
+
onConfirm: (paymentMethod: MarketplacePaymentMethod) => void;
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MarketplaceBuyModal: React.FC<IMarketplaceBuyModalProps> = ({
|
|
19
|
+
goldPrice,
|
|
20
|
+
dcEquivalentPrice,
|
|
21
|
+
dcBalance,
|
|
22
|
+
onConfirm,
|
|
23
|
+
onClose,
|
|
24
|
+
}) => {
|
|
25
|
+
const [selected, setSelected] = useState<MarketplacePaymentMethod>('gold');
|
|
26
|
+
const hasSufficientDC = dcBalance >= dcEquivalentPrice;
|
|
27
|
+
|
|
28
|
+
const stopPropagation = useCallback(
|
|
29
|
+
(e: React.MouseEvent | React.TouchEvent | React.PointerEvent) => {
|
|
30
|
+
e.stopPropagation();
|
|
31
|
+
},
|
|
32
|
+
[]
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const handleConfirm = useCallback(() => {
|
|
36
|
+
onConfirm(selected);
|
|
37
|
+
}, [selected, onConfirm]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<ModalPortal>
|
|
41
|
+
<Overlay onPointerDown={onClose} />
|
|
42
|
+
<ModalContainer>
|
|
43
|
+
<ModalContent
|
|
44
|
+
onClick={stopPropagation as React.MouseEventHandler}
|
|
45
|
+
onTouchStart={stopPropagation as React.TouchEventHandler}
|
|
46
|
+
onPointerDown={stopPropagation as React.PointerEventHandler}
|
|
47
|
+
>
|
|
48
|
+
<Header>
|
|
49
|
+
<Title>Confirm Purchase</Title>
|
|
50
|
+
<CloseButton onPointerDown={onClose} aria-label="Close">
|
|
51
|
+
<FaTimes />
|
|
52
|
+
</CloseButton>
|
|
53
|
+
</Header>
|
|
54
|
+
|
|
55
|
+
<Options>
|
|
56
|
+
<RadioOption
|
|
57
|
+
$selected={selected === 'gold'}
|
|
58
|
+
onPointerDown={() => setSelected('gold')}
|
|
59
|
+
>
|
|
60
|
+
<RadioCircle $selected={selected === 'gold'} />
|
|
61
|
+
<OptionText>
|
|
62
|
+
<OptionLabel>Gold</OptionLabel>
|
|
63
|
+
<OptionSub>{goldPrice.toLocaleString()} gold</OptionSub>
|
|
64
|
+
</OptionText>
|
|
65
|
+
</RadioOption>
|
|
66
|
+
|
|
67
|
+
<RadioOption
|
|
68
|
+
$selected={selected === 'dc'}
|
|
69
|
+
$disabled={!hasSufficientDC}
|
|
70
|
+
onPointerDown={() => hasSufficientDC && setSelected('dc')}
|
|
71
|
+
>
|
|
72
|
+
<RadioCircle $selected={selected === 'dc'} />
|
|
73
|
+
<OptionText>
|
|
74
|
+
<OptionLabel $disabled={!hasSufficientDC}>Definya Coin</OptionLabel>
|
|
75
|
+
<OptionSub>
|
|
76
|
+
{formatDCAmount(dcEquivalentPrice)} DC
|
|
77
|
+
{' '}
|
|
78
|
+
<BalanceHint $insufficient={!hasSufficientDC}>
|
|
79
|
+
({formatDCAmount(dcBalance)} available)
|
|
80
|
+
</BalanceHint>
|
|
81
|
+
</OptionSub>
|
|
82
|
+
</OptionText>
|
|
83
|
+
</RadioOption>
|
|
84
|
+
</Options>
|
|
85
|
+
|
|
86
|
+
<ConfirmRow>
|
|
87
|
+
<Button buttonType={ButtonTypes.RPGUIButton} onPointerDown={handleConfirm}>
|
|
88
|
+
Confirm
|
|
89
|
+
</Button>
|
|
90
|
+
</ConfirmRow>
|
|
91
|
+
</ModalContent>
|
|
92
|
+
</ModalContainer>
|
|
93
|
+
</ModalPortal>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const Overlay = styled.div`
|
|
98
|
+
position: fixed;
|
|
99
|
+
inset: 0;
|
|
100
|
+
background: rgba(0, 0, 0, 0.65);
|
|
101
|
+
z-index: 1000;
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
const ModalContainer = styled.div`
|
|
105
|
+
position: fixed;
|
|
106
|
+
inset: 0;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
z-index: 1001;
|
|
111
|
+
pointer-events: none;
|
|
112
|
+
`;
|
|
113
|
+
|
|
114
|
+
const ModalContent = styled.div`
|
|
115
|
+
background: #1a1a2e;
|
|
116
|
+
border: 2px solid #f59e0b;
|
|
117
|
+
border-radius: 8px;
|
|
118
|
+
padding: 20px 24px 24px;
|
|
119
|
+
min-width: 300px;
|
|
120
|
+
max-width: 90%;
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
gap: 16px;
|
|
124
|
+
pointer-events: auto;
|
|
125
|
+
animation: scaleIn 0.15s ease-out;
|
|
126
|
+
|
|
127
|
+
@keyframes scaleIn {
|
|
128
|
+
from { transform: scale(0.85); opacity: 0; }
|
|
129
|
+
to { transform: scale(1); opacity: 1; }
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
const Header = styled.div`
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: space-between;
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
const Title = styled.h3`
|
|
140
|
+
margin: 0;
|
|
141
|
+
font-family: 'Press Start 2P', cursive;
|
|
142
|
+
font-size: 0.7rem;
|
|
143
|
+
color: #fef08a;
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
const CloseButton = styled.button`
|
|
147
|
+
background: none;
|
|
148
|
+
border: none;
|
|
149
|
+
color: rgba(255, 255, 255, 0.6);
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
font-size: 1rem;
|
|
152
|
+
padding: 4px;
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
|
|
156
|
+
&:hover {
|
|
157
|
+
color: #ffffff;
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
const Options = styled.div`
|
|
162
|
+
display: flex;
|
|
163
|
+
flex-direction: column;
|
|
164
|
+
gap: 8px;
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
const RadioOption = styled.div<{ $selected: boolean; $disabled?: boolean }>`
|
|
168
|
+
display: flex;
|
|
169
|
+
align-items: center;
|
|
170
|
+
gap: 12px;
|
|
171
|
+
padding: 10px 12px;
|
|
172
|
+
border: 1px solid ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(255,255,255,0.15)')};
|
|
173
|
+
border-radius: 6px;
|
|
174
|
+
background: ${({ $selected }) => ($selected ? 'rgba(245,158,11,0.1)' : 'transparent')};
|
|
175
|
+
cursor: ${({ $disabled }) => ($disabled ? 'not-allowed' : 'pointer')};
|
|
176
|
+
opacity: ${({ $disabled }) => ($disabled ? 0.5 : 1)};
|
|
177
|
+
transition: border-color 0.15s, background 0.15s;
|
|
178
|
+
|
|
179
|
+
&:hover {
|
|
180
|
+
border-color: ${({ $disabled }) => ($disabled ? 'rgba(255,255,255,0.15)' : '#f59e0b')};
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
|
|
184
|
+
const RadioCircle = styled.div<{ $selected: boolean }>`
|
|
185
|
+
width: 16px;
|
|
186
|
+
height: 16px;
|
|
187
|
+
border-radius: 50%;
|
|
188
|
+
border: 2px solid ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(255,255,255,0.4)')};
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
justify-content: center;
|
|
192
|
+
flex-shrink: 0;
|
|
193
|
+
|
|
194
|
+
&::after {
|
|
195
|
+
content: '';
|
|
196
|
+
width: 8px;
|
|
197
|
+
height: 8px;
|
|
198
|
+
border-radius: 50%;
|
|
199
|
+
background: #f59e0b;
|
|
200
|
+
opacity: ${({ $selected }) => ($selected ? 1 : 0)};
|
|
201
|
+
transition: opacity 0.15s;
|
|
202
|
+
}
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
const OptionText = styled.div`
|
|
206
|
+
display: flex;
|
|
207
|
+
flex-direction: column;
|
|
208
|
+
gap: 3px;
|
|
209
|
+
`;
|
|
210
|
+
|
|
211
|
+
const OptionLabel = styled.span<{ $disabled?: boolean }>`
|
|
212
|
+
font-family: 'Press Start 2P', cursive;
|
|
213
|
+
font-size: 0.65rem;
|
|
214
|
+
color: ${({ $disabled }) => ($disabled ? 'rgba(255,255,255,0.4)' : '#ffffff')};
|
|
215
|
+
`;
|
|
216
|
+
|
|
217
|
+
const OptionSub = styled.span`
|
|
218
|
+
font-family: 'Press Start 2P', cursive;
|
|
219
|
+
font-size: 0.5rem;
|
|
220
|
+
color: rgba(255, 255, 255, 0.55);
|
|
221
|
+
`;
|
|
222
|
+
|
|
223
|
+
const BalanceHint = styled.span<{ $insufficient: boolean }>`
|
|
224
|
+
color: ${({ $insufficient }) => ($insufficient ? '#ef4444' : 'rgba(255, 255, 255, 0.4)')};
|
|
225
|
+
`;
|
|
226
|
+
|
|
227
|
+
const ConfirmRow = styled.div`
|
|
228
|
+
display: flex;
|
|
229
|
+
justify-content: center;
|
|
230
|
+
margin-top: 4px;
|
|
231
|
+
`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
formatDCAmount,
|
|
2
3
|
getItemTextureKeyPath,
|
|
3
4
|
IEquipmentSet,
|
|
4
5
|
IItem,
|
|
@@ -19,6 +20,7 @@ export interface IMarketPlaceRowsPropos {
|
|
|
19
20
|
atlasIMG: any;
|
|
20
21
|
item: IItem;
|
|
21
22
|
itemPrice: number;
|
|
23
|
+
dcEquivalentPrice?: number;
|
|
22
24
|
equipmentSet?: IEquipmentSet | null;
|
|
23
25
|
scale?: number;
|
|
24
26
|
onMarketPlaceItemBuy?: () => void;
|
|
@@ -31,6 +33,7 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
31
33
|
atlasIMG,
|
|
32
34
|
item,
|
|
33
35
|
itemPrice,
|
|
36
|
+
dcEquivalentPrice,
|
|
34
37
|
equipmentSet,
|
|
35
38
|
scale,
|
|
36
39
|
onMarketPlaceItemBuy,
|
|
@@ -99,11 +102,14 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
99
102
|
</GoldContainer>
|
|
100
103
|
<PriceValue>
|
|
101
104
|
<p>
|
|
102
|
-
<Ellipsis maxLines={1} maxWidth="
|
|
105
|
+
<Ellipsis maxLines={1} maxWidth="120px" fontSize="10px">
|
|
103
106
|
${itemPrice}
|
|
104
107
|
</Ellipsis>
|
|
105
108
|
</p>
|
|
106
109
|
</PriceValue>
|
|
110
|
+
{dcEquivalentPrice !== undefined && (
|
|
111
|
+
<DCPriceLabel>{formatDCAmount(dcEquivalentPrice)} DC</DCPriceLabel>
|
|
112
|
+
)}
|
|
107
113
|
</ItemIconContainer>
|
|
108
114
|
<ButtonContainer>
|
|
109
115
|
<Button
|
|
@@ -176,6 +182,13 @@ const SpriteContainer = styled.div`
|
|
|
176
182
|
left: 0.5rem;
|
|
177
183
|
`;
|
|
178
184
|
|
|
185
|
+
const DCPriceLabel = styled.span`
|
|
186
|
+
margin-left: 8px;
|
|
187
|
+
color: #fef08a;
|
|
188
|
+
font-size: 0.65rem;
|
|
189
|
+
white-space: nowrap;
|
|
190
|
+
`;
|
|
191
|
+
|
|
179
192
|
const PriceValue = styled.div`
|
|
180
193
|
margin-left: 40px;
|
|
181
194
|
`;
|
|
@@ -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
|
},
|
|
@@ -252,7 +256,6 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
252
256
|
{onShowHistory && (
|
|
253
257
|
<CTAButton
|
|
254
258
|
icon={<FaHistory />}
|
|
255
|
-
label="History"
|
|
256
259
|
onClick={onShowHistory}
|
|
257
260
|
/>
|
|
258
261
|
)}
|
|
@@ -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';
|