@rpg-engine/long-bow 0.8.166 → 0.8.168
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/MarketplaceRows.d.ts +2 -1
- package/dist/long-bow.cjs.development.js +179 -53
- 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 +180 -54
- 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 +59 -4
- package/src/components/Marketplace/MarketplaceRows.tsx +60 -27
- package/src/components/Marketplace/MarketplaceSettingsPanel.tsx +26 -31
- package/src/stories/Features/trading/Marketplace.stories.tsx +25 -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.168",
|
|
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
|
+
`;
|
|
@@ -29,6 +29,12 @@ export interface IManagmentPanelProps {
|
|
|
29
29
|
currentPage: number;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const LISTING_CURRENCY_OPTIONS: { value: MarketplaceAcceptedCurrency; label: string }[] = [
|
|
33
|
+
{ value: MarketplaceAcceptedCurrency.GoldOrDc, label: 'Both' },
|
|
34
|
+
{ value: MarketplaceAcceptedCurrency.Gold, label: 'Gold' },
|
|
35
|
+
{ value: MarketplaceAcceptedCurrency.Dc, label: 'DC' },
|
|
36
|
+
];
|
|
37
|
+
|
|
32
38
|
export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
33
39
|
items,
|
|
34
40
|
atlasIMG,
|
|
@@ -49,6 +55,9 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
49
55
|
const [price, setPrice] = useState('');
|
|
50
56
|
const [isCreatingOffer, setIsCreatingOffer] = useState(false);
|
|
51
57
|
const [removingItemId, setRemovingItemId] = useState<string | null>(null);
|
|
58
|
+
const [listingCurrency, setListingCurrency] = useState<MarketplaceAcceptedCurrency>(
|
|
59
|
+
acceptedCurrency || MarketplaceAcceptedCurrency.GoldOrDc
|
|
60
|
+
);
|
|
52
61
|
|
|
53
62
|
const itemsContainer = useRef<HTMLDivElement>(null);
|
|
54
63
|
|
|
@@ -63,7 +72,7 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
63
72
|
onClose={setIsCreatingOffer.bind(null, false)}
|
|
64
73
|
onConfirm={() => {
|
|
65
74
|
if (selectedItemToSell && price && Number(price)) {
|
|
66
|
-
onAddItemToMarketplace(selectedItemToSell, Number(price),
|
|
75
|
+
onAddItemToMarketplace(selectedItemToSell, Number(price), listingCurrency);
|
|
67
76
|
setPrice('');
|
|
68
77
|
onSelectedItemToSellRemove(selectedItemToSell);
|
|
69
78
|
setIsCreatingOffer(false);
|
|
@@ -124,6 +133,19 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
124
133
|
}}
|
|
125
134
|
/>
|
|
126
135
|
</Flex>
|
|
136
|
+
<CurrencyToggleRow>
|
|
137
|
+
<CurrencyToggleLabel>Accept:</CurrencyToggleLabel>
|
|
138
|
+
{LISTING_CURRENCY_OPTIONS.map(opt => (
|
|
139
|
+
<CurrencyToggleButton
|
|
140
|
+
key={opt.value}
|
|
141
|
+
$active={listingCurrency === opt.value}
|
|
142
|
+
onPointerDown={() => setListingCurrency(opt.value)}
|
|
143
|
+
type="button"
|
|
144
|
+
>
|
|
145
|
+
{opt.label}
|
|
146
|
+
</CurrencyToggleButton>
|
|
147
|
+
))}
|
|
148
|
+
</CurrencyToggleRow>
|
|
127
149
|
</PriceInputWrapper>
|
|
128
150
|
</Flex>
|
|
129
151
|
</InnerOptionsWrapper>
|
|
@@ -169,13 +191,14 @@ export const ManagmentPanel: React.FC<IManagmentPanelProps> = ({
|
|
|
169
191
|
</OptionsWrapper>
|
|
170
192
|
|
|
171
193
|
<ItemComponentScrollWrapper id="MarketContainer" ref={itemsContainer}>
|
|
172
|
-
{items?.map(({ item, price, _id }: IMarketplaceItem, index: number) => (
|
|
194
|
+
{items?.map(({ item, price, _id, acceptedCurrency: listingCurrency }: IMarketplaceItem, index: number) => (
|
|
173
195
|
<MarketplaceRows
|
|
174
196
|
key={`${item.key}_${index}`}
|
|
175
197
|
atlasIMG={atlasIMG}
|
|
176
198
|
atlasJSON={atlasJSON}
|
|
177
199
|
item={item}
|
|
178
200
|
itemPrice={price}
|
|
201
|
+
acceptedCurrency={listingCurrency}
|
|
179
202
|
equipmentSet={equipmentSet}
|
|
180
203
|
onMarketPlaceItemRemove={setRemovingItemId.bind(null, _id)}
|
|
181
204
|
/>
|
|
@@ -322,12 +345,44 @@ const ItemComponentScrollWrapper = styled.div`
|
|
|
322
345
|
const SmallCTAButton = styled(CTAButton)`
|
|
323
346
|
padding: 8px 12px;
|
|
324
347
|
height: 32px;
|
|
325
|
-
|
|
348
|
+
|
|
326
349
|
span {
|
|
327
350
|
font-size: 0.65rem;
|
|
328
351
|
}
|
|
329
|
-
|
|
352
|
+
|
|
330
353
|
svg {
|
|
331
354
|
font-size: 1.1rem;
|
|
332
355
|
}
|
|
333
356
|
`;
|
|
357
|
+
|
|
358
|
+
const CurrencyToggleRow = styled.div`
|
|
359
|
+
display: flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
gap: 6px;
|
|
362
|
+
margin-top: 8px;
|
|
363
|
+
`;
|
|
364
|
+
|
|
365
|
+
const CurrencyToggleLabel = styled.span`
|
|
366
|
+
font-size: 0.5rem;
|
|
367
|
+
color: #888;
|
|
368
|
+
text-transform: uppercase;
|
|
369
|
+
letter-spacing: 0.5px;
|
|
370
|
+
white-space: nowrap;
|
|
371
|
+
`;
|
|
372
|
+
|
|
373
|
+
const CurrencyToggleButton = styled.button<{ $active: boolean }>`
|
|
374
|
+
padding: 3px 8px;
|
|
375
|
+
font-family: 'Press Start 2P', cursive !important;
|
|
376
|
+
font-size: 0.45rem !important;
|
|
377
|
+
border-radius: 4px;
|
|
378
|
+
border: 1px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.15)')} !important;
|
|
379
|
+
background: ${({ $active }) => ($active ? 'rgba(245,158,11,0.15)' : 'rgba(0,0,0,0.3)')} !important;
|
|
380
|
+
color: ${({ $active }) => ($active ? '#f59e0b' : '#aaa')} !important;
|
|
381
|
+
cursor: pointer;
|
|
382
|
+
transition: border-color 0.15s, background 0.15s, color 0.15s;
|
|
383
|
+
|
|
384
|
+
&:hover {
|
|
385
|
+
border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.35)')} !important;
|
|
386
|
+
color: ${({ $active }) => ($active ? '#f59e0b' : '#ddd')} !important;
|
|
387
|
+
}
|
|
388
|
+
`;
|
|
@@ -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,20 @@ 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 dcOnlyItem: IMarketplaceItem = {
|
|
86
|
+
_id: 'dc-only-listing-1',
|
|
87
|
+
owner: 'player-99',
|
|
88
|
+
price: 5000,
|
|
89
|
+
acceptedCurrency: MarketplaceAcceptedCurrency.Dc,
|
|
90
|
+
item: {
|
|
91
|
+
...items[0],
|
|
92
|
+
_id: 'dc-item-short-sword',
|
|
93
|
+
key: 'short-sword-dc-exclusive',
|
|
94
|
+
name: 'Voidwalker Blade',
|
|
95
|
+
rarity: 'Rare',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
83
99
|
const Template: Story = () => {
|
|
84
100
|
const [page, setPage] = React.useState(1);
|
|
85
101
|
const [openBuyOrdersPage, setOpenBuyOrdersPage] = React.useState(1);
|
|
@@ -129,12 +145,15 @@ const Template: Story = () => {
|
|
|
129
145
|
atlasIMG={atlasIMG}
|
|
130
146
|
atlasJSON={atlasJSON}
|
|
131
147
|
onClose={() => console.log('close')}
|
|
132
|
-
items={
|
|
133
|
-
item
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
148
|
+
items={[
|
|
149
|
+
...items.map(item => ({
|
|
150
|
+
item,
|
|
151
|
+
price: Math.round(Math.random() * 1000),
|
|
152
|
+
_id: Math.random().toString(),
|
|
153
|
+
owner: Math.random().toString(),
|
|
154
|
+
})),
|
|
155
|
+
dcOnlyItem,
|
|
156
|
+
]}
|
|
138
157
|
equipmentSet={equipmentSetMock}
|
|
139
158
|
onMarketPlaceItemBuy={tradeId => console.log(tradeId)}
|
|
140
159
|
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
|
+
);
|