@rpg-engine/long-bow 0.8.167 → 0.8.169
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/ManagmentPanel.d.ts +1 -0
- package/dist/components/Marketplace/MarketplaceRows.d.ts +2 -1
- package/dist/long-bow.cjs.development.js +186 -54
- 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 +187 -55
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/stories/Features/trading/MarketplaceRows.stories.d.ts +1 -0
- package/package.json +2 -2
- package/src/components/Marketplace/BuyPanel.tsx +98 -27
- package/src/components/Marketplace/ManagmentPanel.tsx +79 -15
- package/src/components/Marketplace/MarketplaceRows.tsx +60 -27
- package/src/components/Marketplace/MarketplaceSettingsPanel.tsx +26 -31
- package/src/stories/Features/trading/Marketplace.stories.tsx +33 -6
- package/src/stories/Features/trading/MarketplaceRows.stories.tsx +15 -0
|
@@ -3,3 +3,4 @@ declare const meta: Meta;
|
|
|
3
3
|
export default meta;
|
|
4
4
|
export declare const Default: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react").ReactFramework, import("@storybook/react").Args>;
|
|
5
5
|
export declare const HighStackQuantity: Story;
|
|
6
|
+
export declare const DCOnly: Story;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpg-engine/long-bow",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.169",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@capacitor/core": "^6.1.0",
|
|
86
86
|
"@rollup/plugin-image": "^2.1.1",
|
|
87
|
-
"@rpg-engine/shared": "0.10.
|
|
87
|
+
"@rpg-engine/shared": "0.10.100",
|
|
88
88
|
"dayjs": "^1.11.2",
|
|
89
89
|
"font-awesome": "^4.7.0",
|
|
90
90
|
"fs-extra": "^10.1.0",
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
IEquipmentSet,
|
|
4
4
|
IMarketplaceBuyOrderItem,
|
|
5
5
|
IMarketplaceItem,
|
|
6
|
+
MarketplaceAcceptedCurrency,
|
|
6
7
|
MarketplaceBuyOrderStatus,
|
|
7
8
|
} from '@rpg-engine/shared';
|
|
8
9
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
@@ -112,6 +113,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
112
113
|
]);
|
|
113
114
|
const [buyingItemId, setBuyingItemId] = useState<string | null>(null);
|
|
114
115
|
const [fulfillingBuyOrderId, setFulfillingBuyOrderId] = useState<string | null>(null);
|
|
116
|
+
const [currencyFilter, setCurrencyFilter] = useState<'gold' | 'dc' | null>(null);
|
|
115
117
|
|
|
116
118
|
const itemsContainer = useRef<HTMLDivElement>(null);
|
|
117
119
|
|
|
@@ -127,9 +129,23 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
127
129
|
const getDCEquivalentPrice = (goldPrice: number): number =>
|
|
128
130
|
dcToGoldSwapRate > 0 ? goldToDC(goldPrice) : 0;
|
|
129
131
|
|
|
132
|
+
const handleBuyClick = (listingId: string): void => {
|
|
133
|
+
// All currencies go through the confirmation modal
|
|
134
|
+
setBuyingItemId(listingId);
|
|
135
|
+
};
|
|
136
|
+
|
|
130
137
|
const groupedItems = useMemo(() => {
|
|
138
|
+
const filtered = currencyFilter === null
|
|
139
|
+
? items
|
|
140
|
+
: items.filter(i => {
|
|
141
|
+
const c = i.acceptedCurrency || MarketplaceAcceptedCurrency.GoldOrDc;
|
|
142
|
+
if (currencyFilter === 'gold') return c === MarketplaceAcceptedCurrency.Gold || c === MarketplaceAcceptedCurrency.GoldOrDc;
|
|
143
|
+
if (currencyFilter === 'dc') return c === MarketplaceAcceptedCurrency.Dc || c === MarketplaceAcceptedCurrency.GoldOrDc;
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
|
|
131
147
|
const groups = new Map<string, IMarketplaceItem[]>();
|
|
132
|
-
for (const entry of
|
|
148
|
+
for (const entry of filtered) {
|
|
133
149
|
const key = entry.item.key;
|
|
134
150
|
if (!groups.has(key)) {
|
|
135
151
|
groups.set(key, []);
|
|
@@ -143,7 +159,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
143
159
|
otherListings: sorted.slice(1),
|
|
144
160
|
};
|
|
145
161
|
});
|
|
146
|
-
}, [items]);
|
|
162
|
+
}, [items, currencyFilter]);
|
|
147
163
|
|
|
148
164
|
const visibleBuyOrders = useMemo(() => {
|
|
149
165
|
const normalizedName = name.trim().toLowerCase();
|
|
@@ -199,30 +215,36 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
199
215
|
|
|
200
216
|
return (
|
|
201
217
|
<>
|
|
202
|
-
{buyingItemId && buyingItem &&
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
setBuyingItemId(null)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
218
|
+
{buyingItemId && buyingItem && (() => {
|
|
219
|
+
const listingCurrency = buyingItem.acceptedCurrency || MarketplaceAcceptedCurrency.GoldOrDc;
|
|
220
|
+
const isDcOnly = listingCurrency === MarketplaceAcceptedCurrency.Dc;
|
|
221
|
+
const showPaymentChoiceModal = listingCurrency === MarketplaceAcceptedCurrency.GoldOrDc && hasDCBalance;
|
|
222
|
+
return showPaymentChoiceModal ? (
|
|
223
|
+
<MarketplaceBuyModal
|
|
224
|
+
goldPrice={buyingItem.price}
|
|
225
|
+
dcEquivalentPrice={getDCEquivalentPrice(buyingItem.price)}
|
|
226
|
+
dcBalance={dcBalance}
|
|
227
|
+
onClose={() => setBuyingItemId(null)}
|
|
228
|
+
onConfirm={(paymentMethod) => {
|
|
229
|
+
onMarketPlaceItemBuy?.(buyingItemId, paymentMethod);
|
|
230
|
+
setBuyingItemId(null);
|
|
231
|
+
enableHotkeys?.();
|
|
232
|
+
}}
|
|
233
|
+
/>
|
|
234
|
+
) : (
|
|
235
|
+
<ConfirmModal
|
|
236
|
+
onClose={setBuyingItemId.bind(null, null)}
|
|
237
|
+
onConfirm={() => {
|
|
238
|
+
onMarketPlaceItemBuy?.(buyingItemId, isDcOnly ? 'dc' : undefined);
|
|
239
|
+
setBuyingItemId(null);
|
|
240
|
+
enableHotkeys?.();
|
|
241
|
+
}}
|
|
242
|
+
message={isDcOnly
|
|
243
|
+
? "Are you sure you want to buy this item with DC?"
|
|
244
|
+
: "Are you sure you want to buy this item?"}
|
|
245
|
+
/>
|
|
246
|
+
);
|
|
247
|
+
})()}
|
|
226
248
|
{fulfillingBuyOrderId && (
|
|
227
249
|
<ConfirmModal
|
|
228
250
|
onClose={setFulfillingBuyOrderId.bind(null, null)}
|
|
@@ -270,6 +292,22 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
270
292
|
</FilterButton>
|
|
271
293
|
</ToolbarRow>
|
|
272
294
|
|
|
295
|
+
{showSellSection && (
|
|
296
|
+
<CurrencyFilterRow>
|
|
297
|
+
<CurrencyFilterLabel>Currency:</CurrencyFilterLabel>
|
|
298
|
+
{(['gold', 'dc'] as const).map(c => (
|
|
299
|
+
<CurrencyFilterButton
|
|
300
|
+
key={c}
|
|
301
|
+
$active={currencyFilter === c}
|
|
302
|
+
onPointerDown={() => setCurrencyFilter(prev => prev === c ? null : c)}
|
|
303
|
+
type="button"
|
|
304
|
+
>
|
|
305
|
+
{c === 'gold' ? 'Gold' : 'DC'}
|
|
306
|
+
</CurrencyFilterButton>
|
|
307
|
+
))}
|
|
308
|
+
</CurrencyFilterRow>
|
|
309
|
+
)}
|
|
310
|
+
|
|
273
311
|
{showFilters && (
|
|
274
312
|
<OptionsWrapper showFilters={showFilters}>
|
|
275
313
|
<WrapperContainer $sell={showSellSection}>
|
|
@@ -430,7 +468,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
430
468
|
dcToGoldSwapRate={dcToGoldSwapRate}
|
|
431
469
|
getDCEquivalentPrice={getDCEquivalentPrice}
|
|
432
470
|
characterId={characterId}
|
|
433
|
-
onBuy={
|
|
471
|
+
onBuy={handleBuyClick}
|
|
434
472
|
onDCCoinClick={onDCCoinClick}
|
|
435
473
|
/>
|
|
436
474
|
))
|
|
@@ -708,3 +746,36 @@ const LoadingText = styled.span`
|
|
|
708
746
|
text-transform: uppercase;
|
|
709
747
|
letter-spacing: 1px;
|
|
710
748
|
`;
|
|
749
|
+
|
|
750
|
+
const CurrencyFilterRow = styled.div`
|
|
751
|
+
display: flex;
|
|
752
|
+
align-items: center;
|
|
753
|
+
gap: 6px;
|
|
754
|
+
width: 95%;
|
|
755
|
+
margin: 0 auto 8px auto;
|
|
756
|
+
`;
|
|
757
|
+
|
|
758
|
+
const CurrencyFilterLabel = styled.span`
|
|
759
|
+
font-size: 0.45rem !important;
|
|
760
|
+
color: #666 !important;
|
|
761
|
+
text-transform: uppercase;
|
|
762
|
+
letter-spacing: 0.5px;
|
|
763
|
+
white-space: nowrap;
|
|
764
|
+
`;
|
|
765
|
+
|
|
766
|
+
const CurrencyFilterButton = styled.button<{ $active: boolean }>`
|
|
767
|
+
padding: 3px 8px;
|
|
768
|
+
font-family: 'Press Start 2P', cursive !important;
|
|
769
|
+
font-size: 0.4rem !important;
|
|
770
|
+
border-radius: 4px;
|
|
771
|
+
border: 1px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.12)')} !important;
|
|
772
|
+
background: ${({ $active }) => ($active ? 'rgba(245,158,11,0.15)' : 'rgba(0,0,0,0.3)')} !important;
|
|
773
|
+
color: ${({ $active }) => ($active ? '#f59e0b' : '#777')} !important;
|
|
774
|
+
cursor: pointer;
|
|
775
|
+
transition: border-color 0.15s, background 0.15s, color 0.15s;
|
|
776
|
+
|
|
777
|
+
&:hover {
|
|
778
|
+
border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.3)')} !important;
|
|
779
|
+
color: ${({ $active }) => ($active ? '#f59e0b' : '#bbb')} !important;
|
|
780
|
+
}
|
|
781
|
+
`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IEquipmentSet, IItem, IMarketplaceItem, MarketplaceAcceptedCurrency } from '@rpg-engine/shared';
|
|
1
|
+
import { goldToDC, IEquipmentSet, IItem, IMarketplaceItem, MarketplaceAcceptedCurrency } from '@rpg-engine/shared';
|
|
2
2
|
import { Coins } from 'pixelarticons/react/Coins';
|
|
3
3
|
import { ShoppingBag } from 'pixelarticons/react/ShoppingBag';
|
|
4
4
|
import React, { useEffect, useRef, useState } from 'react';
|
|
@@ -27,8 +27,15 @@ export interface IManagmentPanelProps {
|
|
|
27
27
|
disableHotkeys?: () => void;
|
|
28
28
|
onMoneyWithdraw: () => void;
|
|
29
29
|
currentPage: number;
|
|
30
|
+
dcToGoldSwapRate?: number;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
const LISTING_CURRENCY_OPTIONS: { value: MarketplaceAcceptedCurrency; label: string }[] = [
|
|
34
|
+
{ value: MarketplaceAcceptedCurrency.GoldOrDc, label: 'Both' },
|
|
35
|
+
{ value: MarketplaceAcceptedCurrency.Gold, label: 'Gold' },
|
|
36
|
+
{ value: MarketplaceAcceptedCurrency.Dc, label: 'DC' },
|
|
37
|
+
];
|
|
38
|
+
|
|
32
39
|
export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
33
40
|
items,
|
|
34
41
|
atlasIMG,
|
|
@@ -45,10 +52,14 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
45
52
|
disableHotkeys,
|
|
46
53
|
onMoneyWithdraw,
|
|
47
54
|
currentPage,
|
|
55
|
+
dcToGoldSwapRate = 0,
|
|
48
56
|
}) => {
|
|
49
57
|
const [price, setPrice] = useState('');
|
|
50
58
|
const [isCreatingOffer, setIsCreatingOffer] = useState(false);
|
|
51
59
|
const [removingItemId, setRemovingItemId] = useState<string | null>(null);
|
|
60
|
+
const [listingCurrency, setListingCurrency] = useState<MarketplaceAcceptedCurrency>(
|
|
61
|
+
acceptedCurrency || MarketplaceAcceptedCurrency.GoldOrDc
|
|
62
|
+
);
|
|
52
63
|
|
|
53
64
|
const itemsContainer = useRef<HTMLDivElement>(null);
|
|
54
65
|
|
|
@@ -63,7 +74,7 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
63
74
|
onClose={setIsCreatingOffer.bind(null, false)}
|
|
64
75
|
onConfirm={() => {
|
|
65
76
|
if (selectedItemToSell && price && Number(price)) {
|
|
66
|
-
onAddItemToMarketplace(selectedItemToSell, Number(price),
|
|
77
|
+
onAddItemToMarketplace(selectedItemToSell, Number(price), listingCurrency);
|
|
67
78
|
setPrice('');
|
|
68
79
|
onSelectedItemToSellRemove(selectedItemToSell);
|
|
69
80
|
setIsCreatingOffer(false);
|
|
@@ -124,6 +135,19 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
124
135
|
}}
|
|
125
136
|
/>
|
|
126
137
|
</Flex>
|
|
138
|
+
<CurrencyToggleRow>
|
|
139
|
+
<CurrencyToggleLabel>Accept:</CurrencyToggleLabel>
|
|
140
|
+
{LISTING_CURRENCY_OPTIONS.map(opt => (
|
|
141
|
+
<CurrencyToggleButton
|
|
142
|
+
key={opt.value}
|
|
143
|
+
$active={listingCurrency === opt.value}
|
|
144
|
+
onPointerDown={() => setListingCurrency(opt.value)}
|
|
145
|
+
type="button"
|
|
146
|
+
>
|
|
147
|
+
{opt.label}
|
|
148
|
+
</CurrencyToggleButton>
|
|
149
|
+
))}
|
|
150
|
+
</CurrencyToggleRow>
|
|
127
151
|
</PriceInputWrapper>
|
|
128
152
|
</Flex>
|
|
129
153
|
</InnerOptionsWrapper>
|
|
@@ -169,17 +193,25 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
169
193
|
</OptionsWrapper>
|
|
170
194
|
|
|
171
195
|
<ItemComponentScrollWrapper id="MarketContainer" ref={itemsContainer}>
|
|
172
|
-
{items?.map(({ item, price, _id }: IMarketplaceItem, index: number) =>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
196
|
+
{items?.map(({ item, price, _id, acceptedCurrency: itemCurrency }: IMarketplaceItem, index: number) => {
|
|
197
|
+
const currency = itemCurrency || MarketplaceAcceptedCurrency.GoldOrDc;
|
|
198
|
+
const isDcOnly = currency === MarketplaceAcceptedCurrency.Dc;
|
|
199
|
+
const showDcPrice = isDcOnly || (dcToGoldSwapRate > 0 && currency !== MarketplaceAcceptedCurrency.Gold);
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<MarketplaceRows
|
|
203
|
+
key={`${item.key}_${index}`}
|
|
204
|
+
atlasIMG={atlasIMG}
|
|
205
|
+
atlasJSON={atlasJSON}
|
|
206
|
+
item={item}
|
|
207
|
+
itemPrice={price}
|
|
208
|
+
dcEquivalentPrice={showDcPrice ? goldToDC(price) : undefined}
|
|
209
|
+
acceptedCurrency={currency}
|
|
210
|
+
equipmentSet={equipmentSet}
|
|
211
|
+
onMarketPlaceItemRemove={setRemovingItemId.bind(null, _id)}
|
|
212
|
+
/>
|
|
213
|
+
);
|
|
214
|
+
})}
|
|
183
215
|
</ItemComponentScrollWrapper>
|
|
184
216
|
</>
|
|
185
217
|
);
|
|
@@ -322,12 +354,44 @@ const ItemComponentScrollWrapper = styled.div`
|
|
|
322
354
|
const SmallCTAButton = styled(CTAButton)`
|
|
323
355
|
padding: 8px 12px;
|
|
324
356
|
height: 32px;
|
|
325
|
-
|
|
357
|
+
|
|
326
358
|
span {
|
|
327
359
|
font-size: 0.65rem;
|
|
328
360
|
}
|
|
329
|
-
|
|
361
|
+
|
|
330
362
|
svg {
|
|
331
363
|
font-size: 1.1rem;
|
|
332
364
|
}
|
|
333
365
|
`;
|
|
366
|
+
|
|
367
|
+
const CurrencyToggleRow = styled.div`
|
|
368
|
+
display: flex;
|
|
369
|
+
align-items: center;
|
|
370
|
+
gap: 6px;
|
|
371
|
+
margin-top: 8px;
|
|
372
|
+
`;
|
|
373
|
+
|
|
374
|
+
const CurrencyToggleLabel = styled.span`
|
|
375
|
+
font-size: 0.5rem;
|
|
376
|
+
color: #888;
|
|
377
|
+
text-transform: uppercase;
|
|
378
|
+
letter-spacing: 0.5px;
|
|
379
|
+
white-space: nowrap;
|
|
380
|
+
`;
|
|
381
|
+
|
|
382
|
+
const CurrencyToggleButton = styled.button<{ $active: boolean }>`
|
|
383
|
+
padding: 3px 8px;
|
|
384
|
+
font-family: 'Press Start 2P', cursive !important;
|
|
385
|
+
font-size: 0.45rem !important;
|
|
386
|
+
border-radius: 4px;
|
|
387
|
+
border: 1px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.15)')} !important;
|
|
388
|
+
background: ${({ $active }) => ($active ? 'rgba(245,158,11,0.15)' : 'rgba(0,0,0,0.3)')} !important;
|
|
389
|
+
color: ${({ $active }) => ($active ? '#f59e0b' : '#aaa')} !important;
|
|
390
|
+
cursor: pointer;
|
|
391
|
+
transition: border-color 0.15s, background 0.15s, color 0.15s;
|
|
392
|
+
|
|
393
|
+
&:hover {
|
|
394
|
+
border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.35)')} !important;
|
|
395
|
+
color: ${({ $active }) => ($active ? '#f59e0b' : '#ddd')} !important;
|
|
396
|
+
}
|
|
397
|
+
`;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
IEquipmentSet,
|
|
5
5
|
IItem,
|
|
6
6
|
IMarketplaceItem,
|
|
7
|
+
MarketplaceAcceptedCurrency,
|
|
7
8
|
} from '@rpg-engine/shared';
|
|
8
9
|
import { Coins } from 'pixelarticons/react/Coins';
|
|
9
10
|
import { Delete } from 'pixelarticons/react/Delete';
|
|
@@ -26,6 +27,7 @@ export interface IMarketPlaceRowsPropos {
|
|
|
26
27
|
item: IItem;
|
|
27
28
|
itemPrice: number;
|
|
28
29
|
dcEquivalentPrice?: number;
|
|
30
|
+
acceptedCurrency?: MarketplaceAcceptedCurrency;
|
|
29
31
|
equipmentSet?: IEquipmentSet | null;
|
|
30
32
|
scale?: number;
|
|
31
33
|
onMarketPlaceItemBuy?: () => void;
|
|
@@ -40,6 +42,7 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
40
42
|
item,
|
|
41
43
|
itemPrice,
|
|
42
44
|
dcEquivalentPrice,
|
|
45
|
+
acceptedCurrency,
|
|
43
46
|
equipmentSet,
|
|
44
47
|
scale,
|
|
45
48
|
onMarketPlaceItemBuy,
|
|
@@ -93,21 +96,29 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
93
96
|
<Ellipsis maxLines={1} maxWidth="200px" fontSize="10px">
|
|
94
97
|
{item.name}
|
|
95
98
|
</Ellipsis>
|
|
99
|
+
{acceptedCurrency === MarketplaceAcceptedCurrency.Dc && (
|
|
100
|
+
<CurrencyBadge $color="#a78bfa">DC only</CurrencyBadge>
|
|
101
|
+
)}
|
|
102
|
+
{acceptedCurrency === MarketplaceAcceptedCurrency.Gold && (
|
|
103
|
+
<CurrencyBadge $color="#fef08a">Gold only</CurrencyBadge>
|
|
104
|
+
)}
|
|
96
105
|
</ItemName>
|
|
97
106
|
<PriceRow>
|
|
98
|
-
|
|
99
|
-
<
|
|
100
|
-
<
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
{acceptedCurrency !== MarketplaceAcceptedCurrency.Dc && (
|
|
108
|
+
<GoldPriceRow>
|
|
109
|
+
<GoldIcon>
|
|
110
|
+
<SimpleTooltip content="Gold Coin" direction="top">
|
|
111
|
+
<SpriteFromAtlas
|
|
112
|
+
atlasIMG={atlasIMG}
|
|
113
|
+
atlasJSON={atlasJSON}
|
|
114
|
+
spriteKey="others/gold-coin-qty-5.png"
|
|
115
|
+
imgScale={1}
|
|
116
|
+
/>
|
|
117
|
+
</SimpleTooltip>
|
|
118
|
+
</GoldIcon>
|
|
119
|
+
<GoldPrice>{itemPrice}</GoldPrice>
|
|
120
|
+
</GoldPriceRow>
|
|
121
|
+
)}
|
|
111
122
|
{dcEquivalentPrice !== undefined && (
|
|
112
123
|
<DCPriceRow $clickable={!!onDCCoinClick} onPointerDown={onDCCoinClick}>
|
|
113
124
|
<DCCoinWrapper>
|
|
@@ -169,20 +180,27 @@ export const GroupedMarketplaceRow: React.FC<IGroupedMarketplaceRowProps> = ({
|
|
|
169
180
|
onBuy,
|
|
170
181
|
onDCCoinClick,
|
|
171
182
|
}) => {
|
|
172
|
-
const makeRow = (listing: IMarketplaceItem) =>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
const makeRow = (listing: IMarketplaceItem) => {
|
|
184
|
+
const listingCurrency = listing.acceptedCurrency || MarketplaceAcceptedCurrency.GoldOrDc;
|
|
185
|
+
const isDcOnly = listingCurrency === MarketplaceAcceptedCurrency.Dc;
|
|
186
|
+
const showDcPrice = isDcOnly || (dcToGoldSwapRate > 0 && listingCurrency !== MarketplaceAcceptedCurrency.Gold);
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<MarketplaceRows
|
|
190
|
+
key={listing._id}
|
|
191
|
+
atlasIMG={atlasIMG}
|
|
192
|
+
atlasJSON={atlasJSON}
|
|
193
|
+
item={listing.item}
|
|
194
|
+
itemPrice={listing.price}
|
|
195
|
+
dcEquivalentPrice={showDcPrice ? getDCEquivalentPrice(listing.price) : undefined}
|
|
196
|
+
acceptedCurrency={listingCurrency}
|
|
197
|
+
equipmentSet={equipmentSet}
|
|
198
|
+
onMarketPlaceItemBuy={() => onBuy(listing._id)}
|
|
199
|
+
onDCCoinClick={isDcOnly ? undefined : onDCCoinClick}
|
|
200
|
+
disabled={listing.owner === characterId}
|
|
201
|
+
/>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
186
204
|
|
|
187
205
|
return (
|
|
188
206
|
<GroupedRowContainer
|
|
@@ -303,6 +321,21 @@ const GemContainer = styled.p`
|
|
|
303
321
|
font-size: ${uiFonts.size.xsmall} !important;
|
|
304
322
|
`;
|
|
305
323
|
|
|
324
|
+
const CurrencyBadge = styled.span<{ $color: string }>`
|
|
325
|
+
display: inline-block;
|
|
326
|
+
margin-left: 6px;
|
|
327
|
+
padding: 1px 4px;
|
|
328
|
+
font-family: 'Press Start 2P', cursive;
|
|
329
|
+
font-size: 0.38rem;
|
|
330
|
+
color: ${({ $color }) => $color};
|
|
331
|
+
border: 1px solid ${({ $color }) => $color};
|
|
332
|
+
border-radius: 3px;
|
|
333
|
+
opacity: 0.85;
|
|
334
|
+
vertical-align: middle;
|
|
335
|
+
text-transform: uppercase;
|
|
336
|
+
letter-spacing: 0.5px;
|
|
337
|
+
`;
|
|
338
|
+
|
|
306
339
|
const RarityContainer = styled.div<{ item: IItem }>`
|
|
307
340
|
border-color: ${({ item }) => rarityColor(item)};
|
|
308
341
|
box-shadow: ${({ item }) => `0 0 5px 8px ${rarityColor(item)}`} inset,
|
|
@@ -12,13 +12,18 @@ export interface IMarketplaceSettingsPanelProps {
|
|
|
12
12
|
const CURRENCY_OPTIONS: { value: MarketplaceAcceptedCurrency; label: string; description: string }[] = [
|
|
13
13
|
{
|
|
14
14
|
value: MarketplaceAcceptedCurrency.GoldOrDc,
|
|
15
|
-
label: '
|
|
16
|
-
description: '
|
|
15
|
+
label: 'Both',
|
|
16
|
+
description: 'Gold & DC',
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
value: MarketplaceAcceptedCurrency.Gold,
|
|
20
|
-
label: 'Gold
|
|
21
|
-
description: '
|
|
20
|
+
label: 'Gold',
|
|
21
|
+
description: 'Gold only',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: MarketplaceAcceptedCurrency.Dc,
|
|
25
|
+
label: 'DC',
|
|
26
|
+
description: 'DC only',
|
|
22
27
|
},
|
|
23
28
|
];
|
|
24
29
|
|
|
@@ -39,11 +44,10 @@ export const MarketplaceSettingsPanel: React.FC<IMarketplaceSettingsPanelProps>
|
|
|
39
44
|
>
|
|
40
45
|
<OptionLabel $active={acceptedCurrency === option.value}>{option.label}</OptionLabel>
|
|
41
46
|
<OptionDescription>{option.description}</OptionDescription>
|
|
42
|
-
{acceptedCurrency === option.value && <ActiveBadge>Active</ActiveBadge>}
|
|
43
47
|
</OptionCard>
|
|
44
48
|
))}
|
|
45
49
|
</OptionsGrid>
|
|
46
|
-
<Hint>
|
|
50
|
+
<Hint>Default currency for new listings.</Hint>
|
|
47
51
|
</Section>
|
|
48
52
|
</Wrapper>
|
|
49
53
|
);
|
|
@@ -72,7 +76,7 @@ const SectionLabel = styled.p`
|
|
|
72
76
|
|
|
73
77
|
const OptionsGrid = styled.div`
|
|
74
78
|
display: grid;
|
|
75
|
-
grid-template-columns:
|
|
79
|
+
grid-template-columns: repeat(3, 1fr);
|
|
76
80
|
gap: 12px;
|
|
77
81
|
`;
|
|
78
82
|
|
|
@@ -80,45 +84,36 @@ const OptionCard = styled.button<{ $active: boolean }>`
|
|
|
80
84
|
position: relative;
|
|
81
85
|
display: flex;
|
|
82
86
|
flex-direction: column;
|
|
83
|
-
align-items:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
gap: 3px;
|
|
90
|
+
padding: 10px 8px;
|
|
91
|
+
background: ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.12)' : 'rgba(0, 0, 0, 0.25)')} !important;
|
|
92
|
+
border: 2px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.08)')} !important;
|
|
88
93
|
border-radius: 6px;
|
|
89
94
|
cursor: pointer;
|
|
90
|
-
text-align:
|
|
95
|
+
text-align: center;
|
|
91
96
|
transition: border-color 0.15s, background 0.15s;
|
|
92
97
|
|
|
93
98
|
&:hover {
|
|
94
|
-
border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.25)')};
|
|
95
|
-
background: ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.15)' : 'rgba(255,255,255,0.04)')};
|
|
99
|
+
border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.25)')} !important;
|
|
100
|
+
background: ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.15)' : 'rgba(255,255,255,0.04)')} !important;
|
|
96
101
|
}
|
|
97
102
|
`;
|
|
98
103
|
|
|
99
104
|
const OptionLabel = styled.span<{ $active: boolean }>`
|
|
100
|
-
font-family: 'Press Start 2P', cursive;
|
|
101
|
-
font-size: 0.
|
|
102
|
-
color: ${({ $active }) => ($active ? '#f59e0b' : '#
|
|
105
|
+
font-family: 'Press Start 2P', cursive !important;
|
|
106
|
+
font-size: 0.55rem !important;
|
|
107
|
+
color: ${({ $active }) => ($active ? '#f59e0b' : '#999')} !important;
|
|
103
108
|
letter-spacing: 0.5px;
|
|
104
109
|
`;
|
|
105
110
|
|
|
106
111
|
const OptionDescription = styled.span`
|
|
107
|
-
font-size: 0.
|
|
108
|
-
color: #
|
|
109
|
-
line-height: 1.
|
|
112
|
+
font-size: 0.45rem !important;
|
|
113
|
+
color: #555 !important;
|
|
114
|
+
line-height: 1.4;
|
|
110
115
|
`;
|
|
111
116
|
|
|
112
|
-
const ActiveBadge = styled.span`
|
|
113
|
-
position: absolute;
|
|
114
|
-
top: 8px;
|
|
115
|
-
right: 10px;
|
|
116
|
-
font-size: 0.45rem;
|
|
117
|
-
color: #f59e0b;
|
|
118
|
-
text-transform: uppercase;
|
|
119
|
-
letter-spacing: 0.5px;
|
|
120
|
-
opacity: 0.8;
|
|
121
|
-
`;
|
|
122
117
|
|
|
123
118
|
const Hint = styled.p`
|
|
124
119
|
margin: 14px 0 0 0;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
IMarketplaceBlueprintSummary,
|
|
3
3
|
IMarketplaceBuyOrderItem,
|
|
4
|
+
IMarketplaceItem,
|
|
4
5
|
IMarketplaceTransaction,
|
|
6
|
+
MarketplaceAcceptedCurrency,
|
|
5
7
|
MarketplaceBuyOrderStatus,
|
|
6
8
|
MarketplaceTransactionType,
|
|
7
9
|
} from '@rpg-engine/shared';
|
|
@@ -80,6 +82,27 @@ const mockTransactions: IMarketplaceTransaction[] = [
|
|
|
80
82
|
{ owner: 'player-1', type: MarketplaceTransactionType.BuyOrderCancelled, goldAmount: 300, itemKey: 'items/leather-armor', itemName: 'Leather Armor', createdAt: daysAgo(10), updatedAt: daysAgo(10) },
|
|
81
83
|
];
|
|
82
84
|
|
|
85
|
+
const currencyForIndex = (i: number): MarketplaceAcceptedCurrency | undefined => {
|
|
86
|
+
if (i === 1) return MarketplaceAcceptedCurrency.Dc;
|
|
87
|
+
if (i === 3) return MarketplaceAcceptedCurrency.Gold;
|
|
88
|
+
if (i === 5) return MarketplaceAcceptedCurrency.Dc;
|
|
89
|
+
return undefined; // defaults to GoldOrDc
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const dcOnlyItem: IMarketplaceItem = {
|
|
93
|
+
_id: 'dc-only-listing-1',
|
|
94
|
+
owner: 'player-99',
|
|
95
|
+
price: 5000,
|
|
96
|
+
acceptedCurrency: MarketplaceAcceptedCurrency.Dc,
|
|
97
|
+
item: {
|
|
98
|
+
...items[0],
|
|
99
|
+
_id: 'dc-item-short-sword',
|
|
100
|
+
key: 'short-sword-dc-exclusive',
|
|
101
|
+
name: 'Voidwalker Blade',
|
|
102
|
+
rarity: 'Rare',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
83
106
|
const Template: Story = () => {
|
|
84
107
|
const [page, setPage] = React.useState(1);
|
|
85
108
|
const [openBuyOrdersPage, setOpenBuyOrdersPage] = React.useState(1);
|
|
@@ -129,12 +152,16 @@ const Template: Story = () => {
|
|
|
129
152
|
atlasIMG={atlasIMG}
|
|
130
153
|
atlasJSON={atlasJSON}
|
|
131
154
|
onClose={() => console.log('close')}
|
|
132
|
-
items={
|
|
133
|
-
item,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
items={[
|
|
156
|
+
...items.map((item, i) => ({
|
|
157
|
+
item,
|
|
158
|
+
price: Math.round(Math.random() * 1000),
|
|
159
|
+
_id: Math.random().toString(),
|
|
160
|
+
owner: Math.random().toString(),
|
|
161
|
+
...(currencyForIndex(i) && { acceptedCurrency: currencyForIndex(i) }),
|
|
162
|
+
})),
|
|
163
|
+
dcOnlyItem,
|
|
164
|
+
]}
|
|
138
165
|
equipmentSet={equipmentSetMock}
|
|
139
166
|
onMarketPlaceItemBuy={tradeId => console.log(tradeId)}
|
|
140
167
|
onDCCoinClick={() => console.log('open store: packs tab')}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MarketplaceAcceptedCurrency, goldToDC } from '@rpg-engine/shared';
|
|
1
2
|
import { Meta, Story } from '@storybook/react';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
import { RPGUIRoot } from '../../..';
|
|
@@ -36,3 +37,17 @@ export const HighStackQuantity: Story = () => (
|
|
|
36
37
|
/>
|
|
37
38
|
</RPGUIRoot>
|
|
38
39
|
);
|
|
40
|
+
|
|
41
|
+
export const DCOnly: Story = () => (
|
|
42
|
+
<RPGUIRoot>
|
|
43
|
+
<MarketplaceRows
|
|
44
|
+
atlasIMG={atlasIMG}
|
|
45
|
+
atlasJSON={atlasJSON}
|
|
46
|
+
itemPrice={5000}
|
|
47
|
+
item={items[1]}
|
|
48
|
+
acceptedCurrency={MarketplaceAcceptedCurrency.Dc}
|
|
49
|
+
dcEquivalentPrice={goldToDC(5000)}
|
|
50
|
+
onMarketPlaceItemBuy={() => {}}
|
|
51
|
+
/>
|
|
52
|
+
</RPGUIRoot>
|
|
53
|
+
);
|