@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.
@@ -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.167",
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.93",
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 items) {
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 && hasDCBalance && (
203
- <MarketplaceBuyModal
204
- goldPrice={buyingItem.price}
205
- dcEquivalentPrice={getDCEquivalentPrice(buyingItem.price)}
206
- dcBalance={dcBalance}
207
- onClose={() => setBuyingItemId(null)}
208
- onConfirm={(paymentMethod) => {
209
- onMarketPlaceItemBuy?.(buyingItemId, paymentMethod);
210
- setBuyingItemId(null);
211
- enableHotkeys?.();
212
- }}
213
- />
214
- )}
215
- {buyingItemId && !hasDCBalance && (
216
- <ConfirmModal
217
- onClose={setBuyingItemId.bind(null, null)}
218
- onConfirm={() => {
219
- onMarketPlaceItemBuy?.(buyingItemId);
220
- setBuyingItemId(null);
221
- enableHotkeys?.();
222
- }}
223
- message="Are you sure you want to buy this item?"
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={setBuyingItemId}
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), acceptedCurrency);
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
- <MarketplaceRows
174
- key={`${item.key}_${index}`}
175
- atlasIMG={atlasIMG}
176
- atlasJSON={atlasJSON}
177
- item={item}
178
- itemPrice={price}
179
- equipmentSet={equipmentSet}
180
- onMarketPlaceItemRemove={setRemovingItemId.bind(null, _id)}
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
- <GoldPriceRow>
99
- <GoldIcon>
100
- <SimpleTooltip content="Gold Coin" direction="top">
101
- <SpriteFromAtlas
102
- atlasIMG={atlasIMG}
103
- atlasJSON={atlasJSON}
104
- spriteKey="others/gold-coin-qty-5.png"
105
- imgScale={1}
106
- />
107
- </SimpleTooltip>
108
- </GoldIcon>
109
- <GoldPrice>{itemPrice}</GoldPrice>
110
- </GoldPriceRow>
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
- <MarketplaceRows
174
- key={listing._id}
175
- atlasIMG={atlasIMG}
176
- atlasJSON={atlasJSON}
177
- item={listing.item}
178
- itemPrice={listing.price}
179
- dcEquivalentPrice={dcToGoldSwapRate > 0 ? getDCEquivalentPrice(listing.price) : undefined}
180
- equipmentSet={equipmentSet}
181
- onMarketPlaceItemBuy={() => onBuy(listing._id)}
182
- onDCCoinClick={onDCCoinClick}
183
- disabled={listing.owner === characterId}
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: 'Gold or DC',
16
- description: 'Accept both Gold and Definya Coin as payment',
15
+ label: 'Both',
16
+ description: 'Gold & DC',
17
17
  },
18
18
  {
19
19
  value: MarketplaceAcceptedCurrency.Gold,
20
- label: 'Gold only',
21
- description: 'Only accept Gold as payment',
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>Buyers will only be able to pay using the currency you accept.</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: 1fr 1fr;
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: flex-start;
84
- gap: 6px;
85
- padding: 14px 16px;
86
- background: ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.12)' : 'rgba(0, 0, 0, 0.25)')};
87
- border: 2px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.08)')};
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: left;
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.6rem;
102
- color: ${({ $active }) => ($active ? '#f59e0b' : '#cccccc')};
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.5rem;
108
- color: #888;
109
- line-height: 1.5;
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={items.map(item => ({
133
- item,
134
- price: Math.round(Math.random() * 1000),
135
- _id: Math.random().toString(),
136
- owner: Math.random().toString(),
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
+ );