@rpg-engine/long-bow 0.8.136 → 0.8.138
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/DCWallet/DCTransferPanel.d.ts +6 -0
- package/dist/components/DCWallet/DCWalletModal.d.ts +4 -0
- package/dist/components/Marketplace/BuyPanel.d.ts +4 -1
- package/dist/components/Marketplace/Marketplace.d.ts +4 -1
- package/dist/components/Marketplace/MarketplaceBuyModal.d.ts +10 -0
- package/dist/components/Marketplace/MarketplaceRows.d.ts +1 -0
- package/dist/components/Store/sections/StorePacksSection.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +427 -140
- 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 +428 -142
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DCWallet/DCHistoryPanel.tsx +130 -68
- package/src/components/DCWallet/DCTransferPanel.tsx +102 -10
- package/src/components/DCWallet/DCWalletModal.tsx +46 -7
- package/src/components/Marketplace/BuyPanel.tsx +29 -2
- package/src/components/Marketplace/Marketplace.tsx +4 -1
- package/src/components/Marketplace/MarketplaceBuyModal.tsx +230 -0
- package/src/components/Marketplace/MarketplaceRows.tsx +41 -16
- package/src/components/Store/Store.tsx +5 -1
- package/src/components/Store/sections/StorePacksSection.tsx +17 -1
- package/src/index.tsx +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import { uiColors } from '../../constants/uiColors';
|
|
4
|
-
import { Dropdown, IOptionsProps } from '../Dropdown';
|
|
5
4
|
import { Pagination } from '../shared/Pagination/Pagination';
|
|
6
5
|
|
|
7
6
|
export interface IDCTransaction {
|
|
@@ -17,24 +16,35 @@ export interface IDCTransaction {
|
|
|
17
16
|
const TRANSACTION_TYPE_LABELS: Record<string, string> = {
|
|
18
17
|
Purchase: 'Purchase',
|
|
19
18
|
Transfer: 'Transfer',
|
|
20
|
-
MarketplaceSale: '
|
|
21
|
-
MarketplacePurchase: '
|
|
22
|
-
StorePurchase: 'Store
|
|
19
|
+
MarketplaceSale: 'Mkt Sale',
|
|
20
|
+
MarketplacePurchase: 'Mkt Buy',
|
|
21
|
+
StorePurchase: 'Store',
|
|
23
22
|
Fee: 'Fee',
|
|
24
23
|
Refund: 'Refund',
|
|
25
|
-
AdminAdjustment: 'Admin
|
|
24
|
+
AdminAdjustment: 'Admin',
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
const TRANSACTION_TYPE_COLORS: Record<string, string> = {
|
|
28
|
+
Purchase: '#22c55e', // green — DC coming in
|
|
29
|
+
Transfer: '#60a5fa', // blue — P2P transfer
|
|
30
|
+
MarketplaceSale: '#fbbf24', // gold — sold item
|
|
31
|
+
MarketplacePurchase: '#f97316', // orange — bought item
|
|
32
|
+
StorePurchase: '#a78bfa', // purple — store
|
|
33
|
+
Fee: '#ef4444', // red — cost
|
|
34
|
+
Refund: '#06b6d4', // cyan — money back
|
|
35
|
+
AdminAdjustment: '#9ca3af', // gray — admin
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const TRANSACTION_TYPE_OPTIONS = [
|
|
39
|
+
{ value: '', label: 'All Types' },
|
|
40
|
+
{ value: 'Purchase', label: 'Purchase' },
|
|
41
|
+
{ value: 'Transfer', label: 'Transfer' },
|
|
42
|
+
{ value: 'MarketplaceSale', label: 'Marketplace Sale' },
|
|
43
|
+
{ value: 'MarketplacePurchase', label: 'Marketplace Buy' },
|
|
44
|
+
{ value: 'StorePurchase', label: 'Store Purchase' },
|
|
45
|
+
{ value: 'Fee', label: 'Fee' },
|
|
46
|
+
{ value: 'Refund', label: 'Refund' },
|
|
47
|
+
{ value: 'AdminAdjustment', label: 'Admin Adjustment' },
|
|
38
48
|
];
|
|
39
49
|
|
|
40
50
|
export interface IDCHistoryPanelProps {
|
|
@@ -54,7 +64,14 @@ export const DCHistoryPanel: React.FC<IDCHistoryPanelProps> = ({
|
|
|
54
64
|
}) => {
|
|
55
65
|
const [selectedType, setSelectedType] = React.useState<string>('');
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
// Auto-load on first mount (when History tab is opened)
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
onRequestHistory(1);
|
|
70
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const handleTypeChange = (e: React.ChangeEvent<HTMLSelectElement>): void => {
|
|
74
|
+
const value = e.target.value;
|
|
58
75
|
setSelectedType(value);
|
|
59
76
|
onRequestHistory(1, value || undefined);
|
|
60
77
|
};
|
|
@@ -65,15 +82,18 @@ export const DCHistoryPanel: React.FC<IDCHistoryPanelProps> = ({
|
|
|
65
82
|
|
|
66
83
|
const formatDate = (dateStr: string): string => {
|
|
67
84
|
const d = new Date(dateStr);
|
|
68
|
-
|
|
69
|
-
return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
|
|
85
|
+
return `${d.getMonth() + 1}/${d.getDate()}/${String(d.getFullYear()).slice(-2)}`;
|
|
70
86
|
};
|
|
71
87
|
|
|
72
88
|
return (
|
|
73
89
|
<PanelContainer>
|
|
74
90
|
<FilterRow>
|
|
75
91
|
<FilterLabel>Filter:</FilterLabel>
|
|
76
|
-
<
|
|
92
|
+
<TypeSelect value={selectedType} onChange={handleTypeChange}>
|
|
93
|
+
{TRANSACTION_TYPE_OPTIONS.map((opt) => (
|
|
94
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
95
|
+
))}
|
|
96
|
+
</TypeSelect>
|
|
77
97
|
</FilterRow>
|
|
78
98
|
|
|
79
99
|
{loading && (
|
|
@@ -92,18 +112,21 @@ export const DCHistoryPanel: React.FC<IDCHistoryPanelProps> = ({
|
|
|
92
112
|
{transactions.map((tx) => {
|
|
93
113
|
const isCredit = tx.amount > 0;
|
|
94
114
|
const label = TRANSACTION_TYPE_LABELS[tx.type] ?? tx.type;
|
|
115
|
+
const color = TRANSACTION_TYPE_COLORS[tx.type] ?? '#f59e0b';
|
|
116
|
+
const subtitle = tx.note ?? (tx.relatedCharacterName ? tx.relatedCharacterName : '');
|
|
95
117
|
|
|
96
118
|
return (
|
|
97
119
|
<TransactionRow key={tx._id}>
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
{
|
|
101
|
-
</
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
120
|
+
<TxLeft>
|
|
121
|
+
<TxType $color={color}>{label}</TxType>
|
|
122
|
+
{subtitle ? <TxNote>{subtitle}</TxNote> : null}
|
|
123
|
+
</TxLeft>
|
|
124
|
+
<TxRight>
|
|
125
|
+
<TxAmount $credit={isCredit}>
|
|
126
|
+
{isCredit ? '+' : ''}{tx.amount} DC
|
|
127
|
+
</TxAmount>
|
|
128
|
+
<TxDate>{formatDate(tx.createdAt)}</TxDate>
|
|
129
|
+
</TxRight>
|
|
107
130
|
</TransactionRow>
|
|
108
131
|
);
|
|
109
132
|
})}
|
|
@@ -132,76 +155,115 @@ const FilterRow = styled.div`
|
|
|
132
155
|
display: flex;
|
|
133
156
|
align-items: center;
|
|
134
157
|
gap: 8px;
|
|
135
|
-
margin-bottom:
|
|
158
|
+
margin-bottom: 2px;
|
|
136
159
|
`;
|
|
137
160
|
|
|
138
161
|
const FilterLabel = styled.span`
|
|
139
|
-
font-size:
|
|
162
|
+
font-size: 7px;
|
|
140
163
|
color: #f59e0b;
|
|
141
164
|
font-family: 'Press Start 2P', cursive;
|
|
142
165
|
white-space: nowrap;
|
|
143
166
|
`;
|
|
144
167
|
|
|
168
|
+
const TypeSelect = styled.select`
|
|
169
|
+
background: rgba(0, 0, 0, 0.7);
|
|
170
|
+
border: 1px solid rgba(245, 158, 11, 0.4);
|
|
171
|
+
border-radius: 3px;
|
|
172
|
+
color: #f59e0b;
|
|
173
|
+
font-size: 7px;
|
|
174
|
+
font-family: 'Press Start 2P', cursive;
|
|
175
|
+
padding: 3px 5px;
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
outline: none;
|
|
178
|
+
flex: 1;
|
|
179
|
+
|
|
180
|
+
option {
|
|
181
|
+
background: #1a1a2e;
|
|
182
|
+
color: #f59e0b;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
&:hover {
|
|
186
|
+
border-color: #f59e0b;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
&:focus {
|
|
190
|
+
border-color: #f59e0b;
|
|
191
|
+
box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.3);
|
|
192
|
+
}
|
|
193
|
+
`;
|
|
194
|
+
|
|
145
195
|
const TransactionList = styled.div`
|
|
146
196
|
display: flex;
|
|
147
197
|
flex-direction: column;
|
|
148
|
-
gap:
|
|
149
|
-
max-height:
|
|
198
|
+
gap: 3px;
|
|
199
|
+
max-height: 260px;
|
|
150
200
|
overflow-y: auto;
|
|
201
|
+
|
|
202
|
+
&::-webkit-scrollbar {
|
|
203
|
+
width: 4px;
|
|
204
|
+
}
|
|
205
|
+
&::-webkit-scrollbar-track {
|
|
206
|
+
background: rgba(0, 0, 0, 0.3);
|
|
207
|
+
}
|
|
208
|
+
&::-webkit-scrollbar-thumb {
|
|
209
|
+
background: rgba(245, 158, 11, 0.4);
|
|
210
|
+
border-radius: 2px;
|
|
211
|
+
}
|
|
151
212
|
`;
|
|
152
213
|
|
|
153
214
|
const TransactionRow = styled.div`
|
|
154
|
-
background: rgba(0, 0, 0, 0.
|
|
155
|
-
border: 1px solid rgba(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
215
|
+
background: rgba(0, 0, 0, 0.35);
|
|
216
|
+
border: 1px solid rgba(245, 158, 11, 0.15);
|
|
217
|
+
border-radius: 2px;
|
|
218
|
+
padding: 5px 7px;
|
|
219
|
+
display: flex;
|
|
220
|
+
align-items: center;
|
|
221
|
+
gap: 8px;
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
const TxLeft = styled.div`
|
|
225
|
+
flex: 1;
|
|
226
|
+
min-width: 0;
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
160
229
|
gap: 2px;
|
|
161
230
|
`;
|
|
162
231
|
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
232
|
+
const TxRight = styled.div`
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
align-items: flex-end;
|
|
236
|
+
gap: 2px;
|
|
237
|
+
flex-shrink: 0;
|
|
238
|
+
`;
|
|
239
|
+
|
|
240
|
+
const TxType = styled.span<{ $color: string }>`
|
|
241
|
+
font-size: 7px;
|
|
242
|
+
color: ${({ $color }) => $color};
|
|
166
243
|
font-family: 'Press Start 2P', cursive;
|
|
167
|
-
grid-column: 1;
|
|
168
|
-
grid-row: 1;
|
|
169
244
|
`;
|
|
170
245
|
|
|
171
246
|
const TxAmount = styled.span<{ $credit: boolean }>`
|
|
172
|
-
font-size:
|
|
247
|
+
font-size: 8px;
|
|
173
248
|
font-family: 'Press Start 2P', cursive;
|
|
174
249
|
color: ${({ $credit }) => ($credit ? uiColors.green : uiColors.red)};
|
|
175
|
-
|
|
176
|
-
grid-column: 2;
|
|
177
|
-
grid-row: 1;
|
|
178
|
-
text-align: right;
|
|
250
|
+
white-space: nowrap;
|
|
179
251
|
`;
|
|
180
252
|
|
|
181
253
|
const TxNote = styled.span`
|
|
182
|
-
font-size:
|
|
254
|
+
font-size: 6px;
|
|
183
255
|
color: ${uiColors.lightGray};
|
|
184
256
|
font-family: 'Press Start 2P', cursive;
|
|
185
|
-
|
|
186
|
-
|
|
257
|
+
white-space: nowrap;
|
|
258
|
+
overflow: hidden;
|
|
259
|
+
text-overflow: ellipsis;
|
|
187
260
|
`;
|
|
188
261
|
|
|
189
262
|
const TxDate = styled.span`
|
|
190
|
-
font-size:
|
|
191
|
-
color:
|
|
192
|
-
font-family: 'Press Start 2P', cursive;
|
|
193
|
-
grid-column: 2;
|
|
194
|
-
grid-row: 2;
|
|
195
|
-
text-align: right;
|
|
196
|
-
`;
|
|
197
|
-
|
|
198
|
-
const TxBalance = styled.span`
|
|
199
|
-
font-size: 7px;
|
|
200
|
-
color: ${uiColors.white};
|
|
263
|
+
font-size: 6px;
|
|
264
|
+
color: rgba(255, 255, 255, 0.4);
|
|
201
265
|
font-family: 'Press Start 2P', cursive;
|
|
202
|
-
|
|
203
|
-
grid-row: 3;
|
|
204
|
-
opacity: 0.7;
|
|
266
|
+
white-space: nowrap;
|
|
205
267
|
`;
|
|
206
268
|
|
|
207
269
|
const LoadingRow = styled.div`
|
|
@@ -219,8 +281,8 @@ const Spinner = styled.div`
|
|
|
219
281
|
border: 3px solid rgba(255, 255, 255, 0.2);
|
|
220
282
|
border-radius: 50%;
|
|
221
283
|
border-top: 3px solid #f59e0b;
|
|
222
|
-
width:
|
|
223
|
-
height:
|
|
284
|
+
width: 16px;
|
|
285
|
+
height: 16px;
|
|
224
286
|
animation: spin 0.8s linear infinite;
|
|
225
287
|
|
|
226
288
|
@keyframes spin {
|
|
@@ -230,7 +292,7 @@ const Spinner = styled.div`
|
|
|
230
292
|
`;
|
|
231
293
|
|
|
232
294
|
const EmptyMessage = styled.div`
|
|
233
|
-
font-size:
|
|
295
|
+
font-size: 8px;
|
|
234
296
|
color: ${uiColors.lightGray};
|
|
235
297
|
font-family: 'Press Start 2P', cursive;
|
|
236
298
|
text-align: center;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { debounce } from 'lodash';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
3
|
import { FaPaperPlane } from 'react-icons/fa';
|
|
3
4
|
import styled from 'styled-components';
|
|
4
5
|
import { uiColors } from '../../constants/uiColors';
|
|
@@ -6,6 +7,11 @@ import { ConfirmModal } from '../ConfirmModal';
|
|
|
6
7
|
import { Input } from '../Input';
|
|
7
8
|
import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
8
9
|
|
|
10
|
+
export interface IDCTransferCharacterResult {
|
|
11
|
+
_id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
export interface IDCTransferPanelProps {
|
|
10
16
|
dcBalance: number;
|
|
11
17
|
transferLoading: boolean;
|
|
@@ -15,6 +21,8 @@ export interface IDCTransferPanelProps {
|
|
|
15
21
|
characterName?: string;
|
|
16
22
|
onInputFocus?: () => void;
|
|
17
23
|
onInputBlur?: () => void;
|
|
24
|
+
onSearchCharacter?: (name: string) => void;
|
|
25
|
+
searchResults?: IDCTransferCharacterResult[];
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
export const DCTransferPanel: React.FC<IDCTransferPanelProps> = ({
|
|
@@ -26,13 +34,33 @@ export const DCTransferPanel: React.FC<IDCTransferPanelProps> = ({
|
|
|
26
34
|
characterName,
|
|
27
35
|
onInputFocus,
|
|
28
36
|
onInputBlur,
|
|
37
|
+
onSearchCharacter,
|
|
38
|
+
searchResults,
|
|
29
39
|
}) => {
|
|
30
40
|
const [recipientName, setRecipientName] = useState<string>('');
|
|
31
41
|
const [amount, setAmount] = useState<string>('');
|
|
32
42
|
const [validationError, setValidationError] = useState<string>('');
|
|
33
43
|
const [showConfirm, setShowConfirm] = useState<boolean>(false);
|
|
44
|
+
const [showDropdown, setShowDropdown] = useState<boolean>(false);
|
|
45
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
34
46
|
const clearResultTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
35
47
|
|
|
48
|
+
const debouncedSearch = useMemo(
|
|
49
|
+
() =>
|
|
50
|
+
debounce((name: string) => {
|
|
51
|
+
if (name.trim().length >= 2) {
|
|
52
|
+
onSearchCharacter?.(name.trim());
|
|
53
|
+
}
|
|
54
|
+
}, 300),
|
|
55
|
+
[onSearchCharacter]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
return () => {
|
|
60
|
+
debouncedSearch.cancel();
|
|
61
|
+
};
|
|
62
|
+
}, [debouncedSearch]);
|
|
63
|
+
|
|
36
64
|
useEffect(() => {
|
|
37
65
|
if (transferResult) {
|
|
38
66
|
if (clearResultTimerRef.current) {
|
|
@@ -93,15 +121,46 @@ export const DCTransferPanel: React.FC<IDCTransferPanelProps> = ({
|
|
|
93
121
|
return (
|
|
94
122
|
<PanelContainer>
|
|
95
123
|
<FieldLabel>Recipient Character Name</FieldLabel>
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
124
|
+
<AutocompleteWrapper ref={dropdownRef}>
|
|
125
|
+
<StyledInput
|
|
126
|
+
type="text"
|
|
127
|
+
value={recipientName}
|
|
128
|
+
onChange={(e) => {
|
|
129
|
+
setRecipientName(e.target.value);
|
|
130
|
+
debouncedSearch(e.target.value);
|
|
131
|
+
setShowDropdown(true);
|
|
132
|
+
}}
|
|
133
|
+
onFocus={() => {
|
|
134
|
+
onInputFocus?.();
|
|
135
|
+
if (recipientName.trim().length >= 2 && searchResults && searchResults.length > 0) {
|
|
136
|
+
setShowDropdown(true);
|
|
137
|
+
}
|
|
138
|
+
}}
|
|
139
|
+
onBlur={() => {
|
|
140
|
+
// Delay to allow dropdown click to register
|
|
141
|
+
setTimeout(() => setShowDropdown(false), 150);
|
|
142
|
+
onInputBlur?.();
|
|
143
|
+
}}
|
|
144
|
+
placeholder="Enter character name"
|
|
145
|
+
disabled={transferLoading}
|
|
146
|
+
autoComplete="off"
|
|
147
|
+
/>
|
|
148
|
+
{showDropdown && searchResults && searchResults.length > 0 && (
|
|
149
|
+
<DropdownList>
|
|
150
|
+
{searchResults.map((char) => (
|
|
151
|
+
<DropdownItem
|
|
152
|
+
key={char._id}
|
|
153
|
+
onPointerDown={() => {
|
|
154
|
+
setRecipientName(char.name);
|
|
155
|
+
setShowDropdown(false);
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
{char.name}
|
|
159
|
+
</DropdownItem>
|
|
160
|
+
))}
|
|
161
|
+
</DropdownList>
|
|
162
|
+
)}
|
|
163
|
+
</AutocompleteWrapper>
|
|
105
164
|
|
|
106
165
|
<FieldLabel>Amount (DC)</FieldLabel>
|
|
107
166
|
<StyledInput
|
|
@@ -215,3 +274,36 @@ const ErrorMessage = styled.div`
|
|
|
215
274
|
color: ${uiColors.red};
|
|
216
275
|
font-family: 'Press Start 2P', cursive;
|
|
217
276
|
`;
|
|
277
|
+
|
|
278
|
+
const AutocompleteWrapper = styled.div`
|
|
279
|
+
position: relative;
|
|
280
|
+
width: 100%;
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
const DropdownList = styled.ul`
|
|
284
|
+
position: absolute;
|
|
285
|
+
top: 100%;
|
|
286
|
+
left: 0;
|
|
287
|
+
right: 0;
|
|
288
|
+
background: rgba(10, 10, 30, 0.95);
|
|
289
|
+
border: 1px solid #f59e0b;
|
|
290
|
+
border-top: none;
|
|
291
|
+
list-style: none;
|
|
292
|
+
padding: 0;
|
|
293
|
+
margin: 0;
|
|
294
|
+
max-height: 120px;
|
|
295
|
+
overflow-y: auto;
|
|
296
|
+
z-index: 10;
|
|
297
|
+
`;
|
|
298
|
+
|
|
299
|
+
const DropdownItem = styled.li`
|
|
300
|
+
padding: 6px 8px;
|
|
301
|
+
font-size: 11px;
|
|
302
|
+
font-family: 'Press Start 2P', cursive;
|
|
303
|
+
color: ${uiColors.white};
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
|
|
306
|
+
&:hover {
|
|
307
|
+
background: rgba(245, 158, 11, 0.3);
|
|
308
|
+
}
|
|
309
|
+
`;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
|
-
import { FaTimes } from 'react-icons/fa';
|
|
2
|
+
import { FaShoppingCart, FaTimes } from 'react-icons/fa';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import { uiColors } from '../../constants/uiColors';
|
|
5
5
|
import ModalPortal from '../Abstractions/ModalPortal';
|
|
6
6
|
import { InternalTabs } from '../InternalTabs/InternalTabs';
|
|
7
7
|
import { DCHistoryPanel, IDCTransaction } from './DCHistoryPanel';
|
|
8
|
-
import { DCTransferPanel } from './DCTransferPanel';
|
|
8
|
+
import { DCTransferPanel, IDCTransferCharacterResult } from './DCTransferPanel';
|
|
9
9
|
|
|
10
10
|
type WalletTabId = 'balance' | 'transfer' | 'history';
|
|
11
11
|
|
|
@@ -22,6 +22,9 @@ export interface IDCWalletModalProps {
|
|
|
22
22
|
characterName?: string;
|
|
23
23
|
onInputFocus?: () => void;
|
|
24
24
|
onInputBlur?: () => void;
|
|
25
|
+
onBuyDC?: () => void;
|
|
26
|
+
onSearchCharacter?: (name: string) => void;
|
|
27
|
+
searchResults?: IDCTransferCharacterResult[];
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
|
|
@@ -37,6 +40,9 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
|
|
|
37
40
|
characterName,
|
|
38
41
|
onInputFocus,
|
|
39
42
|
onInputBlur,
|
|
43
|
+
onBuyDC,
|
|
44
|
+
onSearchCharacter,
|
|
45
|
+
searchResults,
|
|
40
46
|
}) => {
|
|
41
47
|
const [activeTab, setActiveTab] = useState<WalletTabId>('balance');
|
|
42
48
|
|
|
@@ -55,7 +61,12 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
|
|
|
55
61
|
<BalanceContent>
|
|
56
62
|
<BalanceLabel>Your DC Balance</BalanceLabel>
|
|
57
63
|
<BalanceAmount>{dcBalance.toLocaleString()} DC</BalanceAmount>
|
|
58
|
-
|
|
64
|
+
{onBuyDC && (
|
|
65
|
+
<BuyButton onPointerDown={onBuyDC} title="Buy Definya Coins">
|
|
66
|
+
<FaShoppingCart />
|
|
67
|
+
<BuyButtonLabel>Buy DC</BuyButtonLabel>
|
|
68
|
+
</BuyButton>
|
|
69
|
+
)}
|
|
59
70
|
</BalanceContent>
|
|
60
71
|
),
|
|
61
72
|
},
|
|
@@ -72,6 +83,8 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
|
|
|
72
83
|
characterName={characterName}
|
|
73
84
|
onInputFocus={onInputFocus}
|
|
74
85
|
onInputBlur={onInputBlur}
|
|
86
|
+
onSearchCharacter={onSearchCharacter}
|
|
87
|
+
searchResults={searchResults}
|
|
75
88
|
/>
|
|
76
89
|
),
|
|
77
90
|
},
|
|
@@ -110,7 +123,12 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
|
|
|
110
123
|
<InternalTabs
|
|
111
124
|
tabs={tabs}
|
|
112
125
|
activeTab={activeTab}
|
|
113
|
-
onTabChange={(tabId: string) =>
|
|
126
|
+
onTabChange={(tabId: string) => {
|
|
127
|
+
setActiveTab(tabId as WalletTabId);
|
|
128
|
+
if (tabId === 'history') {
|
|
129
|
+
onRequestHistory(1);
|
|
130
|
+
}
|
|
131
|
+
}}
|
|
114
132
|
activeTextColor="#000000"
|
|
115
133
|
activeColor="#fef08a"
|
|
116
134
|
inactiveColor="#6b7280"
|
|
@@ -218,8 +236,29 @@ const BalanceAmount = styled.div`
|
|
|
218
236
|
letter-spacing: 2px;
|
|
219
237
|
`;
|
|
220
238
|
|
|
221
|
-
const
|
|
239
|
+
const BuyButton = styled.button`
|
|
240
|
+
display: flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
gap: 8px;
|
|
243
|
+
margin-top: 8px;
|
|
244
|
+
padding: 10px 20px;
|
|
245
|
+
background: #f59e0b;
|
|
246
|
+
border: none;
|
|
247
|
+
border-radius: 6px;
|
|
248
|
+
color: #000;
|
|
249
|
+
cursor: pointer;
|
|
250
|
+
font-size: 1rem;
|
|
251
|
+
|
|
252
|
+
&:hover {
|
|
253
|
+
background: #fbbf24;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
&:active {
|
|
257
|
+
background: #d97706;
|
|
258
|
+
}
|
|
259
|
+
`;
|
|
260
|
+
|
|
261
|
+
const BuyButtonLabel = styled.span`
|
|
222
262
|
font-family: 'Press Start 2P', cursive;
|
|
223
|
-
font-size:
|
|
224
|
-
color: ${uiColors.lightGray};
|
|
263
|
+
font-size: 8px;
|
|
225
264
|
`;
|
|
@@ -5,6 +5,7 @@ import styled from 'styled-components';
|
|
|
5
5
|
import { ConfirmModal } from '../ConfirmModal';
|
|
6
6
|
import { Dropdown } from '../Dropdown';
|
|
7
7
|
import { Input } from '../Input';
|
|
8
|
+
import { MarketplaceBuyModal, MarketplacePaymentMethod } from './MarketplaceBuyModal';
|
|
8
9
|
import { MarketplaceRows } from './MarketplaceRows';
|
|
9
10
|
import { itemRarityOptions, itemTypeOptions, orderByOptions } from './filters';
|
|
10
11
|
|
|
@@ -26,11 +27,13 @@ export interface IBuyPanelProps {
|
|
|
26
27
|
onChangePriceInput: (value: [number | undefined, number | undefined]) => void;
|
|
27
28
|
scale?: number;
|
|
28
29
|
equipmentSet?: IEquipmentSet | null;
|
|
29
|
-
onMarketPlaceItemBuy?: (marketPlaceItemId: string) => void;
|
|
30
|
+
onMarketPlaceItemBuy?: (marketPlaceItemId: string, paymentMethod?: MarketplacePaymentMethod) => void;
|
|
30
31
|
characterId: string;
|
|
31
32
|
enableHotkeys?: () => void;
|
|
32
33
|
disableHotkeys?: () => void;
|
|
33
34
|
currentPage: number;
|
|
35
|
+
dcBalance?: number;
|
|
36
|
+
dcToGoldSwapRate?: number;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
@@ -50,6 +53,8 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
50
53
|
enableHotkeys,
|
|
51
54
|
disableHotkeys,
|
|
52
55
|
currentPage,
|
|
56
|
+
dcBalance = 0,
|
|
57
|
+
dcToGoldSwapRate = 0,
|
|
53
58
|
}) => {
|
|
54
59
|
const [name, setName] = useState('');
|
|
55
60
|
const [mainLevel, setMainLevel] = useState<
|
|
@@ -70,9 +75,30 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
70
75
|
itemsContainer.current?.scrollTo(0, 0);
|
|
71
76
|
}, [currentPage]);
|
|
72
77
|
|
|
78
|
+
const buyingItem = buyingItemId
|
|
79
|
+
? items.find(i => i._id === buyingItemId)
|
|
80
|
+
: null;
|
|
81
|
+
const hasDCBalance = dcBalance > 0 && dcToGoldSwapRate > 0;
|
|
82
|
+
|
|
83
|
+
const getDCEquivalentPrice = (goldPrice: number): number =>
|
|
84
|
+
dcToGoldSwapRate > 0 ? Math.ceil(goldPrice / dcToGoldSwapRate) : 0;
|
|
85
|
+
|
|
73
86
|
return (
|
|
74
87
|
<>
|
|
75
|
-
{buyingItemId && (
|
|
88
|
+
{buyingItemId && buyingItem && hasDCBalance && (
|
|
89
|
+
<MarketplaceBuyModal
|
|
90
|
+
goldPrice={buyingItem.price}
|
|
91
|
+
dcEquivalentPrice={getDCEquivalentPrice(buyingItem.price)}
|
|
92
|
+
dcBalance={dcBalance}
|
|
93
|
+
onClose={() => setBuyingItemId(null)}
|
|
94
|
+
onConfirm={(paymentMethod) => {
|
|
95
|
+
onMarketPlaceItemBuy?.(buyingItemId, paymentMethod);
|
|
96
|
+
setBuyingItemId(null);
|
|
97
|
+
enableHotkeys?.();
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
{buyingItemId && !hasDCBalance && (
|
|
76
102
|
<ConfirmModal
|
|
77
103
|
onClose={setBuyingItemId.bind(null, null)}
|
|
78
104
|
onConfirm={() => {
|
|
@@ -216,6 +242,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
216
242
|
atlasJSON={atlasJSON}
|
|
217
243
|
item={item}
|
|
218
244
|
itemPrice={price}
|
|
245
|
+
dcEquivalentPrice={dcToGoldSwapRate > 0 ? getDCEquivalentPrice(price) : undefined}
|
|
219
246
|
equipmentSet={equipmentSet}
|
|
220
247
|
onMarketPlaceItemBuy={setBuyingItemId.bind(null, _id)}
|
|
221
248
|
disabled={owner === characterId}
|
|
@@ -5,6 +5,7 @@ import { Button, ButtonTypes } from '../Button';
|
|
|
5
5
|
import { DraggableContainer } from '../DraggableContainer';
|
|
6
6
|
import { Pager } from '../Pager';
|
|
7
7
|
import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
|
|
8
|
+
import { MarketplacePaymentMethod } from './MarketplaceBuyModal';
|
|
8
9
|
import { BuyPanel } from './BuyPanel';
|
|
9
10
|
import { ManagmentPanel } from './ManagmentPanel';
|
|
10
11
|
|
|
@@ -26,7 +27,7 @@ export interface IMarketPlaceProps {
|
|
|
26
27
|
onChangePriceInput: (value: [number | undefined, number | undefined]) => void;
|
|
27
28
|
scale?: number;
|
|
28
29
|
equipmentSet?: IEquipmentSet | null;
|
|
29
|
-
onMarketPlaceItemBuy?: (marketPlaceItemId: string) => void;
|
|
30
|
+
onMarketPlaceItemBuy?: (marketPlaceItemId: string, paymentMethod?: MarketplacePaymentMethod) => void;
|
|
30
31
|
onMarketPlaceItemRemove?: (marketPlaceItemId: string) => void;
|
|
31
32
|
availableGold: number;
|
|
32
33
|
selectedItemToSell: IItem | null;
|
|
@@ -41,6 +42,8 @@ export interface IMarketPlaceProps {
|
|
|
41
42
|
currentPage: number;
|
|
42
43
|
itemsPerPage: number;
|
|
43
44
|
onPageChange: (page: number) => void;
|
|
45
|
+
dcBalance?: number;
|
|
46
|
+
dcToGoldSwapRate?: number;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export const Marketplace: React.FC<IMarketPlaceProps> = props => {
|