@rpg-engine/long-bow 0.8.72 → 0.8.74
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/DailyTasks/DailyTaskItem.d.ts +1 -0
- package/dist/hooks/useCharacterSkinNavigation.d.ts +7 -0
- package/dist/hooks/usePackFiltering.d.ts +7 -0
- package/dist/hooks/useQuantityControl.d.ts +10 -0
- package/dist/hooks/useStoreFiltering.d.ts +11 -0
- package/dist/long-bow.cjs.development.js +629 -272
- 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 +631 -274
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/DailyTasks/DailyRewardsTooltip.tsx +69 -38
- package/src/components/DailyTasks/DailyTaskItem.tsx +272 -76
- package/src/components/DailyTasks/DailyTasks.tsx +230 -46
- package/src/components/DailyTasks/GlobalDailyProgress.tsx +31 -24
- package/src/components/DailyTasks/TaskProgress.tsx +31 -20
- package/src/components/Store/StoreCharacterSkinRow.tsx +63 -45
- package/src/components/Store/StoreItemRow.tsx +51 -53
- package/src/components/Store/sections/StoreItemsSection.tsx +72 -21
- package/src/components/Store/sections/StorePacksSection.tsx +5 -10
- package/src/hooks/useCharacterSkinNavigation.ts +34 -0
- package/src/hooks/usePackFiltering.ts +20 -0
- package/src/hooks/useQuantityControl.ts +41 -0
- package/src/hooks/useStoreFiltering.ts +51 -0
- package/src/mocks/dailyTasks.mocks.ts +6 -6
- package/src/stories/Features/store/Store.stories.tsx +35 -8
|
@@ -3,13 +3,18 @@ import {
|
|
|
3
3
|
ICollectGlobalRewardRequest,
|
|
4
4
|
ICollectRewardRequest,
|
|
5
5
|
TaskType,
|
|
6
|
+
TaskStatus,
|
|
7
|
+
isMobileOrTablet,
|
|
6
8
|
} from '@rpg-engine/shared';
|
|
7
|
-
import React, { useState } from 'react';
|
|
9
|
+
import React, { useState, useMemo } from 'react';
|
|
8
10
|
import styled from 'styled-components';
|
|
11
|
+
import { FaThumbtack, FaClipboardList } from 'react-icons/fa';
|
|
9
12
|
import { uiColors } from '../../constants/uiColors';
|
|
10
|
-
import {
|
|
13
|
+
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
|
11
14
|
import { DraggableContainer } from '../DraggableContainer';
|
|
12
15
|
import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
|
|
16
|
+
import { Dropdown, IOptionsProps as DropdownOption } from '../Dropdown';
|
|
17
|
+
import { SearchBar } from '../shared/SearchBar/SearchBar';
|
|
13
18
|
import { DailyTaskItem } from './DailyTaskItem';
|
|
14
19
|
import { GlobalDailyProgress } from './GlobalDailyProgress';
|
|
15
20
|
import { getTaskIcon } from './utils/dailyTasks.utils';
|
|
@@ -40,11 +45,16 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
|
|
|
40
45
|
globalRewardClaimed = false,
|
|
41
46
|
}): JSX.Element | null => {
|
|
42
47
|
const [localTasks] = React.useState(tasks);
|
|
43
|
-
const
|
|
48
|
+
const isMobile = isMobileOrTablet();
|
|
44
49
|
const [claimedTasks, setClaimedTasks] = useState<string[]>([]);
|
|
45
50
|
const [globalRewardClaimedLocal, setGlobalRewardClaimedLocal] = useState(
|
|
46
51
|
false
|
|
47
52
|
);
|
|
53
|
+
|
|
54
|
+
// Search and filter state
|
|
55
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
56
|
+
const [selectedStatus, setSelectedStatus] = useState('all');
|
|
57
|
+
const [pinnedTasks, setPinnedTasks] = useLocalStorage<string[]>('dailyTasks.pinned', []);
|
|
48
58
|
|
|
49
59
|
const handleClaimReward = (taskKey: string, taskType: TaskType) => {
|
|
50
60
|
onClaimReward({
|
|
@@ -62,8 +72,51 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
|
|
|
62
72
|
const isTaskRewardClaimed = (taskKey: string): boolean => {
|
|
63
73
|
return claimedTasks.includes(taskKey);
|
|
64
74
|
};
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
|
|
76
|
+
const togglePinTask = (taskKey: string) => {
|
|
77
|
+
setPinnedTasks(prev =>
|
|
78
|
+
prev.includes(taskKey)
|
|
79
|
+
? prev.filter(key => key !== taskKey)
|
|
80
|
+
: [...prev, taskKey]
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Filter options using Store pattern
|
|
85
|
+
const statusOptions: DropdownOption[] = [
|
|
86
|
+
{ id: 0, value: 'all', option: 'All' },
|
|
87
|
+
{ id: 1, value: 'pinned', option: 'Pinned' },
|
|
88
|
+
{ id: 2, value: 'completed', option: 'Completed' },
|
|
89
|
+
{ id: 3, value: 'inprogress', option: 'In Progress' },
|
|
90
|
+
{ id: 4, value: 'notstarted', option: 'Not Started' },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
// Filtered tasks using InformationCenter pattern
|
|
94
|
+
const filteredTasks = useMemo(() => {
|
|
95
|
+
let filtered = localTasks.filter(task => {
|
|
96
|
+
const matchesSearch =
|
|
97
|
+
task.name?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
98
|
+
task.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
99
|
+
task.key.toLowerCase().includes(searchQuery.toLowerCase());
|
|
100
|
+
|
|
101
|
+
const matchesStatus =
|
|
102
|
+
selectedStatus === 'all' ||
|
|
103
|
+
(selectedStatus === 'pinned' && pinnedTasks.includes(task.key)) ||
|
|
104
|
+
(selectedStatus === 'completed' && task.status === TaskStatus.Completed) ||
|
|
105
|
+
(selectedStatus === 'inprogress' && task.status === TaskStatus.InProgress) ||
|
|
106
|
+
(selectedStatus === 'notstarted' && task.status === TaskStatus.NotStarted);
|
|
107
|
+
|
|
108
|
+
return matchesSearch && matchesStatus;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Sort with pinned tasks first
|
|
112
|
+
return filtered.sort((a, b) => {
|
|
113
|
+
const aIsPinned = pinnedTasks.includes(a.key);
|
|
114
|
+
const bIsPinned = pinnedTasks.includes(b.key);
|
|
115
|
+
if (aIsPinned && !bIsPinned) return -1;
|
|
116
|
+
if (!aIsPinned && bIsPinned) return 1;
|
|
117
|
+
return 0;
|
|
118
|
+
});
|
|
119
|
+
}, [localTasks, searchQuery, selectedStatus, pinnedTasks]);
|
|
67
120
|
|
|
68
121
|
return (
|
|
69
122
|
<TasksContainer
|
|
@@ -71,12 +124,13 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
|
|
|
71
124
|
onCloseButton={onClose}
|
|
72
125
|
cancelDrag=".tasks-container"
|
|
73
126
|
scale={scale}
|
|
74
|
-
width={
|
|
75
|
-
height={
|
|
127
|
+
width={isMobile ? '100vw' : 'max(50vw, 900px)'}
|
|
128
|
+
height={isMobile ? '100vh' : 'min(85vh, 800px)'}
|
|
129
|
+
isMobile={isMobile}
|
|
76
130
|
>
|
|
77
|
-
<TaskTitle>Daily Tasks</TaskTitle>
|
|
78
|
-
<Container>
|
|
79
|
-
<
|
|
131
|
+
<TaskTitle isMobile={isMobile}>Daily Tasks</TaskTitle>
|
|
132
|
+
<Container isMobile={isMobile}>
|
|
133
|
+
<GlobalProgressFixed>
|
|
80
134
|
<GlobalDailyProgress
|
|
81
135
|
tasks={localTasks}
|
|
82
136
|
onClaimAllRewards={handleClaimGlobalRewards}
|
|
@@ -84,29 +138,79 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
|
|
|
84
138
|
globalRewardClaimed || globalRewardClaimedLocal
|
|
85
139
|
}
|
|
86
140
|
/>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
iconAtlasJSON={iconAtlasJSON}
|
|
96
|
-
iconAtlasIMG={iconAtlasIMG}
|
|
97
|
-
isRewardClaimed={task.claimed || isTaskRewardClaimed(task.key)}
|
|
141
|
+
</GlobalProgressFixed>
|
|
142
|
+
|
|
143
|
+
<SearchHeader>
|
|
144
|
+
<SearchBarContainer>
|
|
145
|
+
<SearchBar
|
|
146
|
+
value={searchQuery}
|
|
147
|
+
onChange={setSearchQuery}
|
|
148
|
+
placeholder="Search tasks..."
|
|
98
149
|
/>
|
|
99
|
-
|
|
150
|
+
</SearchBarContainer>
|
|
151
|
+
<DropdownContainer>
|
|
152
|
+
<Dropdown
|
|
153
|
+
options={statusOptions}
|
|
154
|
+
onChange={setSelectedStatus}
|
|
155
|
+
width="100%"
|
|
156
|
+
/>
|
|
157
|
+
</DropdownContainer>
|
|
158
|
+
</SearchHeader>
|
|
159
|
+
|
|
160
|
+
<TasksList className="tasks-container" isMobile={isMobile}>
|
|
161
|
+
{filteredTasks.length > 0 ? (
|
|
162
|
+
filteredTasks.map(task => (
|
|
163
|
+
<TaskWrapper key={task.key}>
|
|
164
|
+
<DailyTaskItem
|
|
165
|
+
task={task}
|
|
166
|
+
spriteKey={getTaskIcon(task.type, task.difficulty)}
|
|
167
|
+
onClaimReward={handleClaimReward}
|
|
168
|
+
itemsAtlasJSON={itemsAtlasJSON}
|
|
169
|
+
itemsAtlasIMG={itemsAtlasIMG}
|
|
170
|
+
iconAtlasJSON={iconAtlasJSON}
|
|
171
|
+
iconAtlasIMG={iconAtlasIMG}
|
|
172
|
+
isRewardClaimed={task.claimed || isTaskRewardClaimed(task.key)}
|
|
173
|
+
isPinned={pinnedTasks.includes(task.key)}
|
|
174
|
+
/>
|
|
175
|
+
<PinButton
|
|
176
|
+
onClick={() => togglePinTask(task.key)}
|
|
177
|
+
isPinned={pinnedTasks.includes(task.key)}
|
|
178
|
+
>
|
|
179
|
+
<FaThumbtack size={10} />
|
|
180
|
+
</PinButton>
|
|
181
|
+
</TaskWrapper>
|
|
182
|
+
))
|
|
183
|
+
) : (
|
|
184
|
+
<EmptyState>
|
|
185
|
+
<FaClipboardList size={48} />
|
|
186
|
+
<p>
|
|
187
|
+
{searchQuery || selectedStatus !== 'all'
|
|
188
|
+
? 'No tasks match your criteria.'
|
|
189
|
+
: 'No daily tasks available.'}
|
|
190
|
+
</p>
|
|
191
|
+
</EmptyState>
|
|
192
|
+
)}
|
|
100
193
|
</TasksList>
|
|
101
194
|
</Container>
|
|
102
195
|
</TasksContainer>
|
|
103
196
|
);
|
|
104
197
|
};
|
|
105
198
|
|
|
106
|
-
const TasksContainer = styled(DraggableContainer)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
199
|
+
const TasksContainer = styled(DraggableContainer) <{ isMobile: boolean }>`
|
|
200
|
+
${props => props.isMobile ? `
|
|
201
|
+
position: fixed !important;
|
|
202
|
+
top: 0 !important;
|
|
203
|
+
left: 0 !important;
|
|
204
|
+
right: 0 !important;
|
|
205
|
+
bottom: 0 !important;
|
|
206
|
+
width: 100vw !important;
|
|
207
|
+
height: 100vh !important;
|
|
208
|
+
margin: 0 !important;
|
|
209
|
+
z-index: 1000 !important;
|
|
210
|
+
` : `
|
|
211
|
+
margin: 0 auto;
|
|
212
|
+
min-width: 50vw;
|
|
213
|
+
`}
|
|
110
214
|
|
|
111
215
|
.rpgui-container-title {
|
|
112
216
|
width: 100%;
|
|
@@ -114,51 +218,131 @@ const TasksContainer = styled(DraggableContainer)`
|
|
|
114
218
|
justify-content: center;
|
|
115
219
|
align-items: center;
|
|
116
220
|
text-align: center;
|
|
221
|
+
${props => props.isMobile ? 'padding: 8px 0;' : ''}
|
|
117
222
|
}
|
|
118
223
|
|
|
119
224
|
.rpgui-container {
|
|
120
225
|
padding: 0 !important;
|
|
121
226
|
overflow: hidden !important;
|
|
122
|
-
background-color: rgba(30, 30, 30, 0.
|
|
227
|
+
background-color: rgba(30, 30, 30, 0.98) !important;
|
|
228
|
+
${props => props.isMobile ? `
|
|
229
|
+
border-radius: 0 !important;
|
|
230
|
+
height: 100vh !important;
|
|
231
|
+
width: 100vw !important;
|
|
232
|
+
` : ''}
|
|
123
233
|
}
|
|
124
234
|
`;
|
|
125
235
|
|
|
126
|
-
const Container = styled.div
|
|
236
|
+
const Container = styled.div<{ isMobile: boolean }>`
|
|
127
237
|
width: 100%;
|
|
128
|
-
|
|
129
|
-
margin: 0
|
|
130
|
-
padding: 0
|
|
238
|
+
height: 100%;
|
|
239
|
+
margin: 0;
|
|
240
|
+
padding: 0;
|
|
241
|
+
overflow: hidden;
|
|
242
|
+
box-sizing: border-box;
|
|
243
|
+
background-color: transparent;
|
|
244
|
+
display: flex;
|
|
245
|
+
flex-direction: column;
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
const TasksList = styled.div<{ isMobile: boolean }>`
|
|
249
|
+
display: flex;
|
|
250
|
+
flex-direction: column;
|
|
251
|
+
gap: ${props => props.isMobile ? '8px' : '12px'};
|
|
252
|
+
padding: ${props => props.isMobile ? '8px' : '12px'};
|
|
131
253
|
overflow-y: auto;
|
|
254
|
+
flex: 1;
|
|
255
|
+
background-color: transparent;
|
|
256
|
+
|
|
257
|
+
${props => props.isMobile ? `
|
|
258
|
+
-webkit-overflow-scrolling: touch;
|
|
259
|
+
scrollbar-width: thin;
|
|
260
|
+
` : ''}
|
|
261
|
+
`;
|
|
132
262
|
|
|
133
|
-
|
|
263
|
+
const GlobalProgressFixed = styled.div`
|
|
264
|
+
flex-shrink: 0;
|
|
265
|
+
padding: 8px 12px;
|
|
266
|
+
background-color: transparent;
|
|
267
|
+
`;
|
|
134
268
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
269
|
+
const SearchHeader = styled.div`
|
|
270
|
+
display: flex;
|
|
271
|
+
gap: 12px;
|
|
272
|
+
padding: 12px 12px 8px 12px;
|
|
273
|
+
border-bottom: 1px solid ${uiColors.darkGray};
|
|
274
|
+
background: rgba(0, 0, 0, 0.3);
|
|
275
|
+
`;
|
|
138
276
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
277
|
+
const SearchBarContainer = styled.div`
|
|
278
|
+
flex: 3;
|
|
279
|
+
`;
|
|
280
|
+
|
|
281
|
+
const DropdownContainer = styled.div`
|
|
282
|
+
flex: 1;
|
|
283
|
+
min-width: 120px;
|
|
284
|
+
`;
|
|
142
285
|
|
|
143
|
-
|
|
144
|
-
|
|
286
|
+
const TaskWrapper = styled.div`
|
|
287
|
+
position: relative;
|
|
288
|
+
width: 100%;
|
|
289
|
+
`;
|
|
290
|
+
|
|
291
|
+
const PinButton = styled.button<{ isPinned: boolean }>`
|
|
292
|
+
position: absolute;
|
|
293
|
+
top: 8px;
|
|
294
|
+
right: 8px;
|
|
295
|
+
background: rgba(0, 0, 0, 0.7);
|
|
296
|
+
color: ${props => props.isPinned ? uiColors.yellow : uiColors.lightGray};
|
|
297
|
+
border: 1px solid ${props => props.isPinned ? uiColors.yellow : 'transparent'};
|
|
298
|
+
cursor: pointer;
|
|
299
|
+
padding: 4px;
|
|
300
|
+
display: flex;
|
|
301
|
+
align-items: center;
|
|
302
|
+
justify-content: center;
|
|
303
|
+
border-radius: 3px;
|
|
304
|
+
transition: all 0.2s ease;
|
|
305
|
+
z-index: 10;
|
|
306
|
+
width: 20px;
|
|
307
|
+
height: 20px;
|
|
308
|
+
|
|
309
|
+
&:hover {
|
|
310
|
+
color: ${uiColors.yellow};
|
|
311
|
+
background: rgba(0, 0, 0, 0.9);
|
|
312
|
+
border-color: ${uiColors.yellow};
|
|
145
313
|
}
|
|
314
|
+
|
|
315
|
+
${props => props.isPinned && `
|
|
316
|
+
transform: rotate(45deg);
|
|
317
|
+
`}
|
|
146
318
|
`;
|
|
147
319
|
|
|
148
|
-
const
|
|
320
|
+
const EmptyState = styled.div`
|
|
149
321
|
display: flex;
|
|
150
322
|
flex-direction: column;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
323
|
+
align-items: center;
|
|
324
|
+
justify-content: center;
|
|
325
|
+
padding: 60px 20px;
|
|
326
|
+
text-align: center;
|
|
327
|
+
color: ${uiColors.lightGray};
|
|
328
|
+
opacity: 0.7;
|
|
329
|
+
gap: 16px;
|
|
330
|
+
|
|
331
|
+
p {
|
|
332
|
+
margin: 0;
|
|
333
|
+
font-size: 0.9rem;
|
|
334
|
+
line-height: 1.4;
|
|
335
|
+
}
|
|
154
336
|
`;
|
|
155
337
|
|
|
156
|
-
const TaskTitle = styled.h2
|
|
338
|
+
const TaskTitle = styled.h2<{ isMobile: boolean }>`
|
|
157
339
|
color: ${uiColors.yellow} !important;
|
|
158
340
|
text-align: center;
|
|
159
341
|
padding-right: 30px !important;
|
|
160
|
-
font-size: 1.
|
|
342
|
+
font-size: ${props => props.isMobile ? '1rem' : '1.2rem'} !important;
|
|
161
343
|
width: 100%;
|
|
344
|
+
margin: ${props => props.isMobile ? '4px 0' : '8px 0'} !important;
|
|
345
|
+
font-weight: 600;
|
|
162
346
|
`;
|
|
163
347
|
|
|
164
348
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
ICharacterDailyTask,
|
|
3
3
|
ICollectGlobalRewardRequest,
|
|
4
4
|
TaskStatus,
|
|
5
|
+
isMobileOrTablet,
|
|
5
6
|
} from '@rpg-engine/shared';
|
|
6
7
|
import React from 'react';
|
|
7
8
|
import styled from 'styled-components';
|
|
@@ -19,6 +20,7 @@ export const GlobalDailyProgress: React.FC<IGlobalTaskProgressProps> = ({
|
|
|
19
20
|
onClaimAllRewards,
|
|
20
21
|
globalRewardClaimed,
|
|
21
22
|
}) => {
|
|
23
|
+
const isMobile = isMobileOrTablet();
|
|
22
24
|
const totalTasks = tasks.length;
|
|
23
25
|
const completedTasks = tasks.filter(
|
|
24
26
|
task => task.status === TaskStatus.Completed
|
|
@@ -45,11 +47,11 @@ export const GlobalDailyProgress: React.FC<IGlobalTaskProgressProps> = ({
|
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
return (
|
|
48
|
-
<GlobalProgressContainer>
|
|
50
|
+
<GlobalProgressContainer isMobile={isMobile}>
|
|
49
51
|
<HeaderContainer>
|
|
50
52
|
<GlobeIcon>🌍</GlobeIcon>
|
|
51
|
-
<ProgressText>
|
|
52
|
-
Global Tasks
|
|
53
|
+
<ProgressText isMobile={isMobile}>
|
|
54
|
+
{isMobile ? `${completedTasks}/${totalTasks} Complete` : `Global Tasks: ${completedTasks}/${totalTasks}`}
|
|
53
55
|
</ProgressText>
|
|
54
56
|
</HeaderContainer>
|
|
55
57
|
<ProgressBar>
|
|
@@ -63,12 +65,12 @@ export const GlobalDailyProgress: React.FC<IGlobalTaskProgressProps> = ({
|
|
|
63
65
|
buttonType={ButtonTypes.RPGUIButton}
|
|
64
66
|
onPointerDown={handleClaimAll}
|
|
65
67
|
>
|
|
66
|
-
Collect Global Rewards
|
|
68
|
+
{isMobile ? 'Global Rewards' : 'Collect Global Rewards'}
|
|
67
69
|
</Button>
|
|
68
70
|
</CollectWrapper>
|
|
69
71
|
)}
|
|
70
72
|
{shouldShowClaimedMessage && (
|
|
71
|
-
<ClaimedText
|
|
73
|
+
<ClaimedText isMobile={isMobile}>✓ Global Rewards Claimed</ClaimedText>
|
|
72
74
|
)}
|
|
73
75
|
</>
|
|
74
76
|
)}
|
|
@@ -76,26 +78,27 @@ export const GlobalDailyProgress: React.FC<IGlobalTaskProgressProps> = ({
|
|
|
76
78
|
);
|
|
77
79
|
};
|
|
78
80
|
|
|
79
|
-
const GlobalProgressContainer = styled.div
|
|
80
|
-
background: rgba(0, 0, 0, 0.
|
|
81
|
-
border:
|
|
82
|
-
border-radius:
|
|
83
|
-
padding:
|
|
81
|
+
const GlobalProgressContainer = styled.div<{ isMobile: boolean }>`
|
|
82
|
+
background: rgba(0, 0, 0, 0.6) !important;
|
|
83
|
+
border: 1px solid ${uiColors.blue} !important;
|
|
84
|
+
border-radius: ${props => props.isMobile ? '4px' : '6px'};
|
|
85
|
+
padding: ${props => props.isMobile ? '6px' : '8px'};
|
|
84
86
|
display: flex;
|
|
85
87
|
flex-direction: column;
|
|
86
|
-
gap:
|
|
87
|
-
box-shadow: 0 2px
|
|
88
|
+
gap: ${props => props.isMobile ? '6px' : '8px'};
|
|
89
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
90
|
+
margin-bottom: ${props => props.isMobile ? '4px' : '6px'};
|
|
88
91
|
`;
|
|
89
92
|
|
|
90
93
|
const HeaderContainer = styled.div`
|
|
91
94
|
display: flex;
|
|
92
95
|
align-items: center;
|
|
93
|
-
gap:
|
|
94
|
-
margin-bottom:
|
|
96
|
+
gap: 6px;
|
|
97
|
+
margin-bottom: 2px;
|
|
95
98
|
`;
|
|
96
99
|
|
|
97
100
|
const GlobeIcon = styled.span`
|
|
98
|
-
font-size:
|
|
101
|
+
font-size: 1rem !important;
|
|
99
102
|
line-height: 1;
|
|
100
103
|
color: ${uiColors.blue};
|
|
101
104
|
display: flex;
|
|
@@ -103,18 +106,19 @@ const GlobeIcon = styled.span`
|
|
|
103
106
|
justify-content: center;
|
|
104
107
|
`;
|
|
105
108
|
|
|
106
|
-
const ProgressText = styled.div
|
|
109
|
+
const ProgressText = styled.div<{ isMobile: boolean }>`
|
|
107
110
|
color: ${uiColors.white};
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
font-size: ${props => props.isMobile ? '0.75rem' : '0.8rem'};
|
|
112
|
+
font-weight: 500;
|
|
113
|
+
flex: 1;
|
|
110
114
|
line-height: 1.2;
|
|
111
115
|
`;
|
|
112
116
|
|
|
113
117
|
const ProgressBar = styled.div`
|
|
114
118
|
width: 100%;
|
|
115
|
-
height:
|
|
119
|
+
height: 4px;
|
|
116
120
|
background: ${uiColors.darkGray};
|
|
117
|
-
border-radius:
|
|
121
|
+
border-radius: 2px;
|
|
118
122
|
overflow: hidden;
|
|
119
123
|
`;
|
|
120
124
|
|
|
@@ -125,12 +129,15 @@ const ProgressFill = styled.div<{ percentage: number }>`
|
|
|
125
129
|
transition: width 0.3s ease;
|
|
126
130
|
`;
|
|
127
131
|
|
|
128
|
-
const ClaimedText = styled.span
|
|
132
|
+
const ClaimedText = styled.span<{ isMobile: boolean }>`
|
|
129
133
|
color: ${uiColors.green};
|
|
130
|
-
font-size: 0.9rem;
|
|
134
|
+
font-size: ${props => props.isMobile ? '0.8rem' : '0.9rem'};
|
|
131
135
|
text-align: center;
|
|
132
|
-
margin-top: 8px;
|
|
133
136
|
font-weight: bold;
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
gap: 4px;
|
|
134
141
|
`;
|
|
135
142
|
|
|
136
143
|
const CollectWrapper = styled.div`
|
|
@@ -139,6 +146,6 @@ const CollectWrapper = styled.div`
|
|
|
139
146
|
display: flex !important;
|
|
140
147
|
justify-content: center !important;
|
|
141
148
|
align-items: center !important;
|
|
142
|
-
margin:
|
|
149
|
+
margin: 4px 0 !important;
|
|
143
150
|
}
|
|
144
151
|
`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ICharacterDailyTask } from '@rpg-engine/shared';
|
|
1
|
+
import { ICharacterDailyTask, isMobileOrTablet } from '@rpg-engine/shared';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import { uiColors } from '../../constants/uiColors';
|
|
@@ -22,23 +22,25 @@ export const TaskProgress: React.FC<TaskProgressProps> = ({
|
|
|
22
22
|
iconAtlasIMG,
|
|
23
23
|
}) => {
|
|
24
24
|
const { difficulty } = task;
|
|
25
|
+
const isMobile = isMobileOrTablet();
|
|
25
26
|
|
|
26
27
|
return (
|
|
27
|
-
<ProgressContainer>
|
|
28
|
-
<ProgressList>
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
<ProgressContainer isMobile={isMobile}>
|
|
29
|
+
<ProgressList isMobile={isMobile}>
|
|
30
|
+
<ProgressRow>
|
|
31
|
+
<ProgressItem>
|
|
32
|
+
<ProgressLabel>Difficulty:</ProgressLabel>
|
|
33
|
+
<TaskDifficulty difficulty={difficulty}>
|
|
34
|
+
{formatDifficulty(difficulty)}
|
|
35
|
+
</TaskDifficulty>
|
|
36
|
+
</ProgressItem>
|
|
37
|
+
<ProgressItem>
|
|
38
|
+
<ProgressLabel>Status:</ProgressLabel>
|
|
39
|
+
<StatusText color={getStatusInfo(task).color}>
|
|
40
|
+
{getStatusInfo(task).text}
|
|
41
|
+
</StatusText>
|
|
42
|
+
</ProgressItem>
|
|
43
|
+
</ProgressRow>
|
|
42
44
|
|
|
43
45
|
<TaskProgressDetails task={task} />
|
|
44
46
|
|
|
@@ -58,25 +60,34 @@ export const TaskProgress: React.FC<TaskProgressProps> = ({
|
|
|
58
60
|
);
|
|
59
61
|
};
|
|
60
62
|
|
|
61
|
-
const ProgressContainer = styled.div
|
|
63
|
+
const ProgressContainer = styled.div<{ isMobile: boolean }>`
|
|
62
64
|
width: 100%;
|
|
63
65
|
position: relative;
|
|
64
66
|
`;
|
|
65
67
|
|
|
66
|
-
const ProgressList = styled.div
|
|
68
|
+
const ProgressList = styled.div<{ isMobile: boolean }>`
|
|
67
69
|
display: flex;
|
|
68
70
|
flex-direction: column;
|
|
69
|
-
gap:
|
|
71
|
+
gap: ${props => props.isMobile ? '3px' : '4px'};
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const ProgressRow = styled.div`
|
|
75
|
+
display: flex;
|
|
76
|
+
gap: 12px;
|
|
77
|
+
flex-wrap: wrap;
|
|
70
78
|
`;
|
|
71
79
|
|
|
72
80
|
const ProgressItem = styled.div`
|
|
73
81
|
display: flex;
|
|
74
82
|
justify-content: space-between;
|
|
75
83
|
align-items: center;
|
|
84
|
+
flex: 1;
|
|
85
|
+
min-width: 100px;
|
|
76
86
|
`;
|
|
77
87
|
|
|
78
88
|
const ProgressLabel = styled.span`
|
|
79
|
-
color: ${uiColors.
|
|
89
|
+
color: ${uiColors.lightGray} !important;
|
|
90
|
+
font-size: 0.7rem;
|
|
80
91
|
`;
|
|
81
92
|
|
|
82
93
|
const TaskDifficulty = styled.span<{ difficulty: string }>`
|