@rpg-engine/long-bow 0.8.146 → 0.8.149

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.146",
3
+ "version": "0.8.149",
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": "file:../rpg-shared",
87
+ "@rpg-engine/shared": "^0.10.90",
88
88
  "dayjs": "^1.11.2",
89
89
  "font-awesome": "^4.7.0",
90
90
  "fs-extra": "^10.1.0",
@@ -3,7 +3,8 @@ import {
3
3
  IMarketplaceBlueprintSummary,
4
4
  ItemSubType,
5
5
  } from '@rpg-engine/shared';
6
- import React, { useState } from 'react';
6
+ import { debounce } from 'lodash';
7
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
8
  import { FaTimes } from 'react-icons/fa';
8
9
  import styled, { keyframes } from 'styled-components';
9
10
  import { Dropdown } from '../Dropdown';
@@ -24,6 +25,8 @@ export interface IBlueprintSearchModalProps {
24
25
  isLoading: boolean;
25
26
  atlasJSON: any;
26
27
  atlasIMG: any;
28
+ enableHotkeys?: () => void;
29
+ disableHotkeys?: () => void;
27
30
  }
28
31
 
29
32
  const BLUEPRINTS_PER_PAGE = 10;
@@ -58,27 +61,49 @@ export const BlueprintSearchModal: React.FC<IBlueprintSearchModalProps> = ({
58
61
  isLoading,
59
62
  atlasJSON,
60
63
  atlasIMG,
64
+ enableHotkeys,
65
+ disableHotkeys,
61
66
  }) => {
62
67
  const [searchName, setSearchName] = useState('');
63
68
  const [selectedType, setSelectedType] = useState('');
64
69
  const [selectedSubType, setSelectedSubType] = useState('');
65
70
 
66
- if (!isOpen) return null;
71
+ const searchNameRef = useRef(searchName);
72
+ const selectedTypeRef = useRef(selectedType);
73
+ const selectedSubTypeRef = useRef(selectedSubType);
74
+
75
+ searchNameRef.current = searchName;
76
+ selectedTypeRef.current = selectedType;
77
+ selectedSubTypeRef.current = selectedSubType;
78
+
79
+ const triggerSearch = useCallback(
80
+ (overrides?: Partial<{ name: string; itemType: string; itemSubType: string; page: number }>): void => {
81
+ onSearch({
82
+ npcId: '',
83
+ name: overrides?.name ?? searchNameRef.current,
84
+ itemType: overrides?.itemType ?? selectedTypeRef.current,
85
+ itemSubType: overrides?.itemSubType ?? selectedSubTypeRef.current,
86
+ page: overrides?.page ?? 1,
87
+ limit: BLUEPRINTS_PER_PAGE,
88
+ });
89
+ },
90
+ [onSearch]
91
+ );
67
92
 
68
- const triggerSearch = (overrides?: Partial<{ name: string; itemType: string; itemSubType: string; page: number }>): void => {
69
- onSearch({
70
- npcId: '',
71
- name: overrides?.name ?? searchName,
72
- itemType: overrides?.itemType ?? selectedType,
73
- itemSubType: overrides?.itemSubType ?? selectedSubType,
74
- page: overrides?.page ?? 1,
75
- limit: BLUEPRINTS_PER_PAGE,
76
- });
77
- };
93
+ const debouncedNameSearch = useMemo(
94
+ () => debounce((name: string) => triggerSearch({ name, page: 1 }), 300),
95
+ [triggerSearch]
96
+ );
97
+
98
+ useEffect(() => {
99
+ return () => { debouncedNameSearch.cancel(); };
100
+ }, [debouncedNameSearch]);
101
+
102
+ if (!isOpen) return null;
78
103
 
79
104
  const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
80
105
  setSearchName(e.target.value);
81
- triggerSearch({ name: e.target.value, page: 1 });
106
+ debouncedNameSearch(e.target.value);
82
107
  };
83
108
 
84
109
  const handleTypeChange = (value: string): void => {
@@ -126,6 +151,8 @@ export const BlueprintSearchModal: React.FC<IBlueprintSearchModalProps> = ({
126
151
  value={searchName}
127
152
  onChange={handleNameChange}
128
153
  placeholder="Search by name..."
154
+ onFocus={disableHotkeys}
155
+ onBlur={enableHotkeys}
129
156
  />
130
157
  </InputWrapper>
131
158
 
@@ -145,17 +172,22 @@ export const BlueprintSearchModal: React.FC<IBlueprintSearchModalProps> = ({
145
172
  </FiltersRow>
146
173
 
147
174
  <ResultsWrapper>
148
- {isLoading ? (
149
- <EmptyState>Loading...</EmptyState>
150
- ) : blueprints.length === 0 ? (
151
- <EmptyState>No blueprints found</EmptyState>
175
+ {blueprints.length === 0 && !isLoading ? (
176
+ <EmptyState>No items found</EmptyState>
152
177
  ) : (
153
- <BlueprintTable
154
- blueprints={blueprints}
155
- atlasJSON={atlasJSON}
156
- atlasIMG={atlasIMG}
157
- onSelect={onSelect}
158
- />
178
+ <ResultsContent $dimmed={isLoading}>
179
+ <BlueprintTable
180
+ blueprints={blueprints}
181
+ atlasJSON={atlasJSON}
182
+ atlasIMG={atlasIMG}
183
+ onSelect={onSelect}
184
+ />
185
+ </ResultsContent>
186
+ )}
187
+ {isLoading && (
188
+ <LoadingOverlay>
189
+ <LoadingText>Loading...</LoadingText>
190
+ </LoadingOverlay>
159
191
  )}
160
192
  </ResultsWrapper>
161
193
 
@@ -268,18 +300,40 @@ const StyledDropdown = styled(Dropdown)`
268
300
  `;
269
301
 
270
302
  const ResultsWrapper = styled.div`
303
+ position: relative;
271
304
  overflow-y: auto;
272
- max-height: 320px;
305
+ height: 320px;
273
306
  background: rgba(0, 0, 0, 0.2);
274
307
  border: 1px solid rgba(255, 255, 255, 0.05);
275
308
  border-radius: 4px;
276
309
  `;
277
310
 
311
+ const ResultsContent = styled.div<{ $dimmed?: boolean }>`
312
+ opacity: ${(p) => (p.$dimmed ? 0.4 : 1)};
313
+ transition: opacity 0.15s ease;
314
+ `;
315
+
316
+ const LoadingOverlay = styled.div`
317
+ position: absolute;
318
+ inset: 0;
319
+ display: flex;
320
+ align-items: center;
321
+ justify-content: center;
322
+ pointer-events: none;
323
+ `;
324
+
325
+ const LoadingText = styled.span`
326
+ font-size: 0.55rem;
327
+ color: #f59e0b;
328
+ text-transform: uppercase;
329
+ letter-spacing: 1px;
330
+ `;
331
+
278
332
  const EmptyState = styled.div`
279
333
  display: flex;
280
334
  align-items: center;
281
335
  justify-content: center;
282
- height: 100px;
336
+ height: 100%;
283
337
  font-size: 0.55rem;
284
338
  color: #666;
285
339
  text-transform: uppercase;
@@ -25,6 +25,8 @@ export interface IBuyOrderDetailsModalProps {
25
25
  onConfirm: () => void;
26
26
  atlasJSON: any;
27
27
  atlasIMG: any;
28
+ enableHotkeys?: () => void;
29
+ disableHotkeys?: () => void;
28
30
  }
29
31
 
30
32
  const scaleIn = keyframes`
@@ -53,6 +55,8 @@ export const BuyOrderDetailsModal: React.FC<IBuyOrderDetailsModalProps> = ({
53
55
  onConfirm,
54
56
  atlasJSON,
55
57
  atlasIMG,
58
+ enableHotkeys,
59
+ disableHotkeys,
56
60
  }) => {
57
61
  if (!isOpen || !blueprint) return null;
58
62
 
@@ -107,6 +111,8 @@ export const BuyOrderDetailsModal: React.FC<IBuyOrderDetailsModalProps> = ({
107
111
  placeholder="Qty"
108
112
  type="number"
109
113
  min={1}
114
+ onFocus={disableHotkeys}
115
+ onBlur={enableHotkeys}
110
116
  />
111
117
  </FieldRow>
112
118
 
@@ -118,6 +124,8 @@ export const BuyOrderDetailsModal: React.FC<IBuyOrderDetailsModalProps> = ({
118
124
  placeholder="Max gold"
119
125
  type="number"
120
126
  min={1}
127
+ onFocus={disableHotkeys}
128
+ onBlur={enableHotkeys}
121
129
  />
122
130
  </FieldRow>
123
131
 
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  IMarketplaceBlueprintSummary,
3
3
  IMarketplaceBuyOrderItem,
4
+ MarketplaceBuyOrderStatus,
4
5
  } from '@rpg-engine/shared';
5
6
  import { Search } from 'pixelarticons/react/Search';
6
7
  import React, { useEffect, useState } from 'react';
7
8
  import styled from 'styled-components';
8
9
  import { Pager } from '../Pager';
9
10
  import { CTAButton } from '../shared/CTAButton/CTAButton';
11
+ import { SegmentedToggle } from '../shared/SegmentedToggle';
10
12
  import { rarityColor } from '../Item/Inventory/ItemSlotRarity';
11
13
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
12
14
  import { BuyOrderDetailsModal } from './BuyOrderDetailsModal';
@@ -31,6 +33,8 @@ export interface IBuyOrderPanelProps {
31
33
  yourBuyOrdersPage: number;
32
34
  onYourBuyOrdersPageChange: (page: number) => void;
33
35
  onCancelBuyOrder: (buyOrderId: string) => void;
36
+ enableHotkeys?: () => void;
37
+ disableHotkeys?: () => void;
34
38
  }
35
39
 
36
40
  const BUY_ORDERS_PER_PAGE = 5;
@@ -54,6 +58,8 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
54
58
  yourBuyOrdersPage,
55
59
  onYourBuyOrdersPageChange,
56
60
  onCancelBuyOrder,
61
+ enableHotkeys,
62
+ disableHotkeys,
57
63
  } = props;
58
64
 
59
65
  // Local blueprint display: cleared immediately on Place Request so the
@@ -61,6 +67,7 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
61
67
  // the prop. Cancel keeps it shown so the user can reopen the modal.
62
68
  const [displayedBlueprint, setDisplayedBlueprint] = useState(selectedBlueprint);
63
69
  const [isDetailsOpen, setIsDetailsOpen] = useState(!!selectedBlueprint);
70
+ const [statusFilter, setStatusFilter] = useState<'Active' | 'Fulfilled' | 'Expired' | 'All'>('Active');
64
71
 
65
72
  // Sync when consumer provides a new blueprint (e.g. after search selection)
66
73
  useEffect(() => {
@@ -76,11 +83,13 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
76
83
  setDisplayedBlueprint(undefined);
77
84
  setIsDetailsOpen(false);
78
85
  onCloseDetails?.();
86
+ enableHotkeys?.();
79
87
  };
80
88
 
81
89
  // Cancel: just close the modal, keep blueprint displayed for reopening
82
90
  const handleCloseDetails = () => {
83
91
  setIsDetailsOpen(false);
92
+ enableHotkeys?.();
84
93
  };
85
94
 
86
95
  return (
@@ -100,6 +109,8 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
100
109
  onConfirm={handleConfirm}
101
110
  atlasJSON={atlasJSON}
102
111
  atlasIMG={atlasIMG}
112
+ enableHotkeys={enableHotkeys}
113
+ disableHotkeys={disableHotkeys}
103
114
  />
104
115
  )}
105
116
 
@@ -134,23 +145,40 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
134
145
  </FormRow>
135
146
 
136
147
  {/* ── Scrollable lists ──────────────────────────────── */}
148
+ <FilterRow>
149
+ <SectionTitle>Your Buy Requests</SectionTitle>
150
+ <SegmentedToggle
151
+ options={[
152
+ { id: 'Active', label: 'Active' },
153
+ { id: 'Fulfilled', label: 'Fulfilled' },
154
+ { id: 'Expired', label: 'Expired' },
155
+ { id: 'All', label: 'All' },
156
+ ]}
157
+ activeId={statusFilter}
158
+ onChange={(id) => setStatusFilter(id as typeof statusFilter)}
159
+ />
160
+ </FilterRow>
137
161
  <ScrollArea id="MarketContainer">
138
162
  <Section>
139
- <SectionTitle>Your Buy Requests</SectionTitle>
140
- {yourBuyOrders.length === 0 ? (
141
- <EmptyState>No requests yet</EmptyState>
142
- ) : (
143
- yourBuyOrders.map((order) => (
144
- <BuyOrderRow
145
- key={order._id}
146
- buyOrder={order}
147
- atlasJSON={atlasJSON}
148
- atlasIMG={atlasIMG}
149
- isOwn
150
- onCancel={onCancelBuyOrder}
151
- />
152
- ))
153
- )}
163
+ {(() => {
164
+ const filtered = statusFilter === 'All'
165
+ ? yourBuyOrders
166
+ : yourBuyOrders.filter(o => o.status === MarketplaceBuyOrderStatus[statusFilter as keyof typeof MarketplaceBuyOrderStatus]);
167
+ return filtered.length === 0 ? (
168
+ <EmptyState>No requests yet</EmptyState>
169
+ ) : (
170
+ filtered.map((order) => (
171
+ <BuyOrderRow
172
+ key={order._id}
173
+ buyOrder={order}
174
+ atlasJSON={atlasJSON}
175
+ atlasIMG={atlasIMG}
176
+ isOwn
177
+ onCancel={onCancelBuyOrder}
178
+ />
179
+ ))
180
+ );
181
+ })()}
154
182
  {yourBuyOrdersTotal > BUY_ORDERS_PER_PAGE && (
155
183
  <PagerRow>
156
184
  <Pager
@@ -188,6 +216,13 @@ const FormRow = styled.div`
188
216
  overflow: visible;
189
217
  `;
190
218
 
219
+ const FilterRow = styled.div`
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: space-between;
223
+ padding: 0 4px;
224
+ `;
225
+
191
226
  const ScrollArea = styled.div`
192
227
  display: flex;
193
228
  flex-direction: column;
@@ -8,7 +8,7 @@ import {
8
8
  import React, { useEffect, useMemo, useRef, useState } from 'react';
9
9
  import { AiFillCaretRight } from 'react-icons/ai';
10
10
  import { SortVertical } from 'pixelarticons/react/SortVertical';
11
- import styled from 'styled-components';
11
+ import styled, { keyframes } from 'styled-components';
12
12
  import { ConfirmModal } from '../ConfirmModal';
13
13
  import { Dropdown } from '../Dropdown';
14
14
  import { Input } from '../Input';
@@ -61,6 +61,7 @@ export interface IBuyPanelProps {
61
61
  openBuyOrdersTotal?: number;
62
62
  openBuyOrdersPage?: number;
63
63
  onOpenBuyOrdersPageChange?: (page: number) => void;
64
+ isLoading?: boolean;
64
65
  }
65
66
 
66
67
  export const BuyPanel: React.FC<IBuyPanelProps> = ({
@@ -90,6 +91,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
90
91
  openBuyOrdersTotal = 0,
91
92
  openBuyOrdersPage = 1,
92
93
  onOpenBuyOrdersPageChange,
94
+ isLoading = false,
93
95
  }) => {
94
96
  const [name, setName] = useState('');
95
97
  const [showFilters, setShowFilters] = useState(false);
@@ -369,7 +371,13 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
369
371
  )}
370
372
 
371
373
  <ItemComponentScrollWrapper id="MarketContainer" ref={itemsContainer}>
372
- {!hasVisibleContent && <EmptyState>No offers match the current filters.</EmptyState>}
374
+ {isLoading && !hasVisibleContent && (
375
+ <LoadingState>
376
+ <Spinner />
377
+ <LoadingText>Loading marketplace...</LoadingText>
378
+ </LoadingState>
379
+ )}
380
+ {!isLoading && !hasVisibleContent && <EmptyState>No offers match the current filters.</EmptyState>}
373
381
 
374
382
  {showSellSection && (
375
383
  <MarketSection>
@@ -645,3 +653,33 @@ const StyledDropdown = styled(Dropdown)`
645
653
  margin: 0px !important;
646
654
  width: 100% !important;
647
655
  `;
656
+
657
+ const spin = keyframes`
658
+ to { transform: rotate(360deg); }
659
+ `;
660
+
661
+ const LoadingState = styled.div`
662
+ display: flex;
663
+ flex-direction: column;
664
+ align-items: center;
665
+ justify-content: center;
666
+ gap: 14px;
667
+ height: 100%;
668
+ min-height: 160px;
669
+ `;
670
+
671
+ const Spinner = styled.div`
672
+ width: 28px;
673
+ height: 28px;
674
+ border: 3px solid rgba(245, 158, 11, 0.2);
675
+ border-top-color: #f59e0b;
676
+ border-radius: 50%;
677
+ animation: ${spin} 0.7s linear infinite;
678
+ `;
679
+
680
+ const LoadingText = styled.span`
681
+ font-size: 0.48rem;
682
+ color: #8a8a8a;
683
+ text-transform: uppercase;
684
+ letter-spacing: 1px;
685
+ `;
@@ -160,7 +160,7 @@ export const HistoryPanel: React.FC<IHistoryPanelProps> = ({
160
160
 
161
161
  {tx.goldAmount > 0 && (
162
162
  <PriceSection>
163
- {tx.currency === 'dc' ? (
163
+ {tx.metadata?.['currency'] === 'dc' ? (
164
164
  // Show DC only
165
165
  <DCPriceRow>
166
166
  {atlasIMG && atlasJSON && (
@@ -65,6 +65,7 @@ export interface IMarketPlaceProps {
65
65
  acceptedCurrency?: MarketplaceAcceptedCurrency;
66
66
  onAcceptedCurrencyChange?: (value: MarketplaceAcceptedCurrency) => void;
67
67
  onActiveTabChange?: (tab: string) => void;
68
+ isLoading?: boolean;
68
69
 
69
70
  // Buy Order props
70
71
  buyOrderSelectedBlueprint?: IMarketplaceBlueprintSummary;
@@ -164,6 +165,7 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
164
165
 
165
166
  const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
166
167
  setIsBlueprintSearchOpen(false);
168
+ props.enableHotkeys?.();
167
169
  onBlueprintSelect?.(blueprint);
168
170
  };
169
171
 
@@ -239,10 +241,12 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
239
241
  yourBuyOrdersPage={yourBuyOrdersPage}
240
242
  onYourBuyOrdersPageChange={onYourBuyOrdersPageChange ?? (() => {})}
241
243
  onCancelBuyOrder={onCancelBuyOrder ?? (() => {})}
244
+ enableHotkeys={props.enableHotkeys}
245
+ disableHotkeys={props.disableHotkeys}
242
246
  />
243
247
  <BlueprintSearchModal
244
248
  isOpen={isBlueprintSearchOpen}
245
- onClose={() => setIsBlueprintSearchOpen(false)}
249
+ onClose={() => { setIsBlueprintSearchOpen(false); props.enableHotkeys?.(); }}
246
250
  onSelect={handleBlueprintSelect}
247
251
  onSearch={onBlueprintSearch ?? (() => {})}
248
252
  blueprints={blueprintSearchResults}
@@ -251,6 +255,8 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
251
255
  isLoading={blueprintSearchIsLoading}
252
256
  atlasJSON={props.atlasJSON}
253
257
  atlasIMG={props.atlasIMG}
258
+ enableHotkeys={props.enableHotkeys}
259
+ disableHotkeys={props.disableHotkeys}
254
260
  />
255
261
  </>
256
262
  )}
@@ -41,7 +41,7 @@ const mockTransactions: IMarketplaceTransaction[] = [
41
41
  itemKey: 'items/iron-sword.png',
42
42
  itemName: 'Iron Sword',
43
43
  counterpartName: 'SomePlayer',
44
- currency: 'gold',
44
+ metadata: { currency: 'gold' },
45
45
  createdAt: daysAgo(0),
46
46
  updatedAt: daysAgo(0),
47
47
  },
@@ -52,7 +52,7 @@ const mockTransactions: IMarketplaceTransaction[] = [
52
52
  itemKey: 'items/leather-armor.png',
53
53
  itemName: 'Leather Armor',
54
54
  counterpartName: 'AnotherPlayer',
55
- currency: 'dc', // DC sale
55
+ metadata: { currency: 'dc' },
56
56
  createdAt: daysAgo(1),
57
57
  updatedAt: daysAgo(1),
58
58
  },
@@ -63,7 +63,7 @@ const mockTransactions: IMarketplaceTransaction[] = [
63
63
  itemKey: 'items/angelic-sword.png',
64
64
  itemName: 'Angelic Sword',
65
65
  counterpartName: 'DCBuyer',
66
- currency: 'dc', // DC purchase
66
+ metadata: { currency: 'dc' },
67
67
  createdAt: daysAgo(2),
68
68
  updatedAt: daysAgo(2),
69
69
  },
@@ -49,9 +49,9 @@ const mockOpenBuyOrders: IMarketplaceBuyOrderItem[] = [
49
49
  ];
50
50
 
51
51
  const mockTransactions: IMarketplaceTransaction[] = [
52
- { owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 450, itemKey: 'items/broad-sword', itemName: 'Broad Sword', counterpartName: 'MerchantKing', currency: 'gold', createdAt: daysAgo(1), updatedAt: daysAgo(1) },
53
- { owner: 'player-1', type: MarketplaceTransactionType.Sale, goldAmount: 1200, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'ShadowBlade', currency: 'dc', createdAt: daysAgo(2), updatedAt: daysAgo(2) },
54
- { owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 275000, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'DCBuyer', currency: 'dc', createdAt: daysAgo(3), updatedAt: daysAgo(3) },
52
+ { owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 450, itemKey: 'items/broad-sword', itemName: 'Broad Sword', counterpartName: 'MerchantKing', metadata: { currency: 'gold' }, createdAt: daysAgo(1), updatedAt: daysAgo(1) },
53
+ { owner: 'player-1', type: MarketplaceTransactionType.Sale, goldAmount: 1200, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'ShadowBlade', metadata: { currency: 'dc' }, createdAt: daysAgo(2), updatedAt: daysAgo(2) },
54
+ { owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 275000, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'DCBuyer', metadata: { currency: 'dc' }, createdAt: daysAgo(3), updatedAt: daysAgo(3) },
55
55
  { owner: 'player-1', type: MarketplaceTransactionType.ListingFee, goldAmount: 60, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', createdAt: daysAgo(2), updatedAt: daysAgo(2) },
56
56
  { owner: 'player-1', type: MarketplaceTransactionType.Withdrawal, goldAmount: 3200, createdAt: daysAgo(4), updatedAt: daysAgo(4) },
57
57
  { owner: 'player-1', type: MarketplaceTransactionType.Expired, goldAmount: 0, itemKey: 'items/leather-armor', itemName: 'Leather Armor', createdAt: daysAgo(7), updatedAt: daysAgo(7) },