@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/dist/components/Marketplace/BlueprintSearchModal.d.ts +2 -0
- package/dist/components/Marketplace/BuyOrderDetailsModal.d.ts +2 -0
- package/dist/components/Marketplace/BuyOrderPanel.d.ts +2 -0
- package/dist/components/Marketplace/BuyPanel.d.ts +1 -0
- package/dist/components/Marketplace/Marketplace.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +188 -88
- 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 +188 -88
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Marketplace/BlueprintSearchModal.tsx +79 -25
- package/src/components/Marketplace/BuyOrderDetailsModal.tsx +8 -0
- package/src/components/Marketplace/BuyOrderPanel.tsx +50 -15
- package/src/components/Marketplace/BuyPanel.tsx +40 -2
- package/src/components/Marketplace/HistoryPanel.tsx +1 -1
- package/src/components/Marketplace/Marketplace.tsx +7 -1
- package/src/stories/Features/marketplace/HistoryPanel.stories.tsx +3 -3
- package/src/stories/Features/trading/Marketplace.stories.tsx +3 -3
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.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": "
|
|
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
|
|
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
|
-
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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>
|
|
150
|
-
) : blueprints.length === 0 ? (
|
|
151
|
-
<EmptyState>No blueprints found</EmptyState>
|
|
175
|
+
{blueprints.length === 0 && !isLoading ? (
|
|
176
|
+
<EmptyState>No items found</EmptyState>
|
|
152
177
|
) : (
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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 &&
|
|
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',
|
|
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',
|
|
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) },
|