@rpg-engine/long-bow 0.8.74 → 0.8.76

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.74",
3
+ "version": "0.8.76",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -5,7 +5,7 @@ import {
5
5
  TaskType,
6
6
  } from '@rpg-engine/shared';
7
7
  import React from 'react';
8
- import styled, { keyframes, css } from 'styled-components';
8
+ import styled, { css, keyframes } from 'styled-components';
9
9
  import { uiColors } from '../../constants/uiColors';
10
10
  import { Button, ButtonTypes } from '../Button';
11
11
  import { Ellipsis } from '../shared/Ellipsis';
@@ -36,6 +36,7 @@ export const DailyTaskItem: React.FC<IDailyTaskItemProps> = ({
36
36
  iconAtlasJSON,
37
37
  iconAtlasIMG,
38
38
  isRewardClaimed,
39
+ isPinned,
39
40
  }) => {
40
41
  const isMobile = isMobileOrTablet();
41
42
 
@@ -43,19 +44,22 @@ export const DailyTaskItem: React.FC<IDailyTaskItemProps> = ({
43
44
  const isInProgress = task.status === TaskStatus.InProgress;
44
45
  const isNotStarted = task.status === TaskStatus.NotStarted;
45
46
  const isClaimed = task.claimed || isRewardClaimed;
47
+ const isChallenge = task.difficulty?.toLowerCase() === 'challenge';
46
48
 
47
49
  const handleClaimReward = () => {
48
50
  onClaimReward(task.key, task.type);
49
51
  };
50
52
 
51
53
  return (
52
- <TaskContainer
54
+ <TaskContainer
53
55
  isMobile={isMobile}
54
56
  isCompleted={isCompleted}
55
57
  isInProgress={isInProgress}
56
58
  isNotStarted={isNotStarted}
59
+ isPinned={isPinned}
60
+ isChallenge={isChallenge}
57
61
  >
58
- <TaskHeader>
62
+ <TaskHeader isMobile={isMobile}>
59
63
  <TaskHeaderLeft>
60
64
  {iconAtlasJSON && iconAtlasIMG && (
61
65
  <IconWrapper>
@@ -99,7 +103,7 @@ export const DailyTaskItem: React.FC<IDailyTaskItemProps> = ({
99
103
  </TaskHeaderRight>
100
104
  </TaskHeader>
101
105
 
102
- <TaskBody>
106
+ <TaskBody isMobile={isMobile}>
103
107
  <TaskDescription isMobile={isMobile}>
104
108
  <Ellipsis maxWidth="100%" maxLines={isMobile ? 2 : 1}>
105
109
  {task.description}
@@ -122,7 +126,7 @@ export const DailyTaskItem: React.FC<IDailyTaskItemProps> = ({
122
126
  </TaskBody>
123
127
 
124
128
  {isCompleted && !isClaimed && (
125
- <TaskFooter>
129
+ <TaskFooter isMobile={isMobile}>
126
130
  <Button
127
131
  buttonType={ButtonTypes.RPGUIButton}
128
132
  onPointerDown={handleClaimReward}
@@ -147,24 +151,25 @@ const pulseAnimation = keyframes`
147
151
  }
148
152
  `;
149
153
 
150
- const TaskContainer = styled.div<{
154
+ const TaskContainer = styled.div<{
151
155
  isMobile: boolean;
152
156
  isCompleted: boolean;
153
157
  isInProgress: boolean;
154
158
  isNotStarted: boolean;
155
159
  isPinned?: boolean;
160
+ isChallenge?: boolean;
156
161
  }>`
157
- background: rgba(0, 0, 0, 0.6) !important;
158
- border: 1px solid ${props =>
159
- props.isCompleted ? uiColors.green :
160
- props.isInProgress ? uiColors.yellow :
161
- uiColors.darkGray
162
+ background: ${props =>
163
+ props.isChallenge ? 'rgb(209, 39, 42)' :
164
+ props.isPinned ? 'rgba(255, 215, 0, 0.1)' :
165
+ 'rgba(0, 0, 0, 0.6)'
162
166
  } !important;
167
+ border: 1px solid ${uiColors.darkGray} !important;
163
168
  border-radius: ${props => props.isMobile ? '4px' : '6px'};
164
- padding: ${props => props.isMobile ? '6px' : '8px'};
169
+ padding: ${props => props.isMobile ? '10px' : '14px'};
165
170
  display: flex;
166
171
  flex-direction: column;
167
- gap: ${props => props.isMobile ? '4px' : '6px'};
172
+ gap: ${props => props.isMobile ? '8px' : '12px'};
168
173
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
169
174
  transition: all 0.2s ease;
170
175
  font-style: normal;
@@ -173,11 +178,7 @@ const TaskContainer = styled.div<{
173
178
  box-sizing: border-box;
174
179
 
175
180
  /* Task status visual states */
176
- opacity: ${props =>
177
- props.isCompleted ? 1 :
178
- props.isInProgress ? 1 :
179
- 0.6
180
- };
181
+ opacity: 1;
181
182
 
182
183
  ${props => props.isNotStarted && `
183
184
  filter: grayscale(0.7);
@@ -192,26 +193,21 @@ const TaskContainer = styled.div<{
192
193
  }
193
194
 
194
195
  &:hover {
195
- background: ${props =>
196
- props.isPinned ? 'rgba(255, 215, 0, 0.25)' : 'rgba(0, 0, 0, 0.7)'
197
- } !important;
198
- border-color: ${props =>
199
- props.isPinned ? uiColors.yellow :
200
- props.isCompleted ? uiColors.green :
201
- props.isInProgress ? uiColors.yellow :
202
- uiColors.yellow
203
- };
204
- opacity: ${props => props.isNotStarted ? 0.8 : 1};
196
+ background: ${props =>
197
+ props.isPinned ? 'rgba(255, 215, 0, 0.25)' : 'rgba(0, 0, 0, 0.7)'
198
+ } !important;
199
+ border-color: ${uiColors.yellow};
200
+ opacity: 1;
205
201
  }
206
202
  `;
207
203
 
208
- const TaskHeader = styled.div`
204
+ const TaskHeader = styled.div<{ isMobile?: boolean }>`
209
205
  display: flex;
210
206
  width: 100%;
211
207
  justify-content: space-between;
212
208
  align-items: flex-start;
213
209
  border-bottom: 1px solid ${uiColors.darkGray};
214
- padding-bottom: 6px;
210
+ padding-bottom: ${props => props.isMobile ? '8px' : '10px'};
215
211
  min-height: 36px;
216
212
  `;
217
213
 
@@ -270,7 +266,7 @@ const TaskDifficulty = styled.span<{ difficulty: string }>`
270
266
  `;
271
267
 
272
268
  const StatusText = styled.span<{ color: string }>`
273
- color: ${props => props.color};
269
+ color: ${props => props.color} !important;
274
270
  font-size: 0.65rem;
275
271
  font-weight: 500;
276
272
  `;
@@ -286,23 +282,24 @@ const ClaimedBadge = styled.div<{ isMobile: boolean }>`
286
282
  justify-content: center;
287
283
  font-size: ${props => props.isMobile ? '10px' : '12px'};
288
284
  font-weight: bold;
285
+ margin-left: 8px;
289
286
  `;
290
287
 
291
- const TaskBody = styled.div`
288
+ const TaskBody = styled.div<{ isMobile?: boolean }>`
292
289
  display: flex;
293
290
  flex-direction: column;
294
291
  gap: 8px;
295
- padding: 6px 0;
292
+ padding: ${props => props.isMobile ? '4px 0' : '6px 0'};
296
293
  `;
297
294
 
298
295
  const RewardsSection = styled.div`
299
296
  margin-top: 4px;
300
297
  `;
301
298
 
302
- const TaskFooter = styled.div`
299
+ const TaskFooter = styled.div<{ isMobile?: boolean }>`
303
300
  display: flex;
304
301
  justify-content: center;
305
- padding-top: 6px;
302
+ padding-top: ${props => props.isMobile ? '8px' : '10px'};
306
303
  border-top: 1px solid ${uiColors.darkGray};
307
304
  `;
308
305
 
@@ -2,18 +2,18 @@ import {
2
2
  ICharacterDailyTask,
3
3
  ICollectGlobalRewardRequest,
4
4
  ICollectRewardRequest,
5
- TaskType,
6
5
  TaskStatus,
6
+ TaskType,
7
7
  isMobileOrTablet,
8
8
  } from '@rpg-engine/shared';
9
- import React, { useState, useMemo } from 'react';
9
+ import React, { useMemo, useState } from 'react';
10
+ import { FaClipboardList, FaThumbtack } from 'react-icons/fa';
10
11
  import styled from 'styled-components';
11
- import { FaThumbtack, FaClipboardList } from 'react-icons/fa';
12
12
  import { uiColors } from '../../constants/uiColors';
13
13
  import { useLocalStorage } from '../../hooks/useLocalStorage';
14
14
  import { DraggableContainer } from '../DraggableContainer';
15
- import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
16
15
  import { Dropdown, IOptionsProps as DropdownOption } from '../Dropdown';
16
+ import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
17
17
  import { SearchBar } from '../shared/SearchBar/SearchBar';
18
18
  import { DailyTaskItem } from './DailyTaskItem';
19
19
  import { GlobalDailyProgress } from './GlobalDailyProgress';
@@ -50,7 +50,7 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
50
50
  const [globalRewardClaimedLocal, setGlobalRewardClaimedLocal] = useState(
51
51
  false
52
52
  );
53
-
53
+
54
54
  // Search and filter state
55
55
  const [searchQuery, setSearchQuery] = useState('');
56
56
  const [selectedStatus, setSelectedStatus] = useState('all');
@@ -72,15 +72,15 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
72
72
  const isTaskRewardClaimed = (taskKey: string): boolean => {
73
73
  return claimedTasks.includes(taskKey);
74
74
  };
75
-
75
+
76
76
  const togglePinTask = (taskKey: string) => {
77
- setPinnedTasks(prev =>
77
+ setPinnedTasks(prev =>
78
78
  prev.includes(taskKey)
79
79
  ? prev.filter(key => key !== taskKey)
80
80
  : [...prev, taskKey]
81
81
  );
82
82
  };
83
-
83
+
84
84
  // Filter options using Store pattern
85
85
  const statusOptions: DropdownOption[] = [
86
86
  { id: 0, value: 'all', option: 'All' },
@@ -89,32 +89,45 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
89
89
  { id: 3, value: 'inprogress', option: 'In Progress' },
90
90
  { id: 4, value: 'notstarted', option: 'Not Started' },
91
91
  ];
92
-
92
+
93
93
  // Filtered tasks using InformationCenter pattern
94
94
  const filteredTasks = useMemo(() => {
95
95
  let filtered = localTasks.filter(task => {
96
- const matchesSearch =
96
+ const matchesSearch =
97
97
  task.name?.toLowerCase().includes(searchQuery.toLowerCase()) ||
98
98
  task.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
99
99
  task.key.toLowerCase().includes(searchQuery.toLowerCase());
100
-
101
- const matchesStatus =
100
+
101
+ const matchesStatus =
102
102
  selectedStatus === 'all' ||
103
103
  (selectedStatus === 'pinned' && pinnedTasks.includes(task.key)) ||
104
104
  (selectedStatus === 'completed' && task.status === TaskStatus.Completed) ||
105
105
  (selectedStatus === 'inprogress' && task.status === TaskStatus.InProgress) ||
106
106
  (selectedStatus === 'notstarted' && task.status === TaskStatus.NotStarted);
107
-
107
+
108
108
  return matchesSearch && matchesStatus;
109
109
  });
110
-
111
- // Sort with pinned tasks first
110
+
111
+ // Sort by relevance: pinned first, then by status (in progress, completed, not started)
112
112
  return filtered.sort((a, b) => {
113
113
  const aIsPinned = pinnedTasks.includes(a.key);
114
114
  const bIsPinned = pinnedTasks.includes(b.key);
115
+
116
+ // Pinned tasks always come first
115
117
  if (aIsPinned && !bIsPinned) return -1;
116
118
  if (!aIsPinned && bIsPinned) return 1;
117
- return 0;
119
+
120
+ // If both are pinned or both are not pinned, sort by status priority
121
+ const statusPriority = {
122
+ [TaskStatus.InProgress]: 1,
123
+ [TaskStatus.Completed]: 2,
124
+ [TaskStatus.NotStarted]: 3,
125
+ };
126
+
127
+ const aPriority = statusPriority[a.status] || 4;
128
+ const bPriority = statusPriority[b.status] || 4;
129
+
130
+ return aPriority - bPriority;
118
131
  });
119
132
  }, [localTasks, searchQuery, selectedStatus, pinnedTasks]);
120
133
 
@@ -139,7 +152,7 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
139
152
  }
140
153
  />
141
154
  </GlobalProgressFixed>
142
-
155
+
143
156
  <SearchHeader>
144
157
  <SearchBarContainer>
145
158
  <SearchBar
@@ -156,7 +169,7 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
156
169
  />
157
170
  </DropdownContainer>
158
171
  </SearchHeader>
159
-
172
+
160
173
  <TasksList className="tasks-container" isMobile={isMobile}>
161
174
  {filteredTasks.length > 0 ? (
162
175
  filteredTasks.map(task => (
@@ -249,7 +262,7 @@ const TasksList = styled.div<{ isMobile: boolean }>`
249
262
  display: flex;
250
263
  flex-direction: column;
251
264
  gap: ${props => props.isMobile ? '8px' : '12px'};
252
- padding: ${props => props.isMobile ? '8px' : '12px'};
265
+ padding: ${props => props.isMobile ? '8px 8px 40px 8px' : '12px 12px 40px 12px'};
253
266
  overflow-y: auto;
254
267
  flex: 1;
255
268
  background-color: transparent;
@@ -262,14 +275,18 @@ const TasksList = styled.div<{ isMobile: boolean }>`
262
275
 
263
276
  const GlobalProgressFixed = styled.div`
264
277
  flex-shrink: 0;
265
- padding: 8px 12px;
278
+ padding: 12px;
266
279
  background-color: transparent;
267
280
  `;
268
281
 
269
282
  const SearchHeader = styled.div`
270
283
  display: flex;
271
- gap: 12px;
272
- padding: 12px 12px 8px 12px;
284
+ gap: 12px;
285
+ justify-content: center;
286
+ align-items: center;
287
+ margin-left: 12px;
288
+ margin-right: 12px;
289
+ padding: 0.25rem;
273
290
  border-bottom: 1px solid ${uiColors.darkGray};
274
291
  background: rgba(0, 0, 0, 0.3);
275
292
  `;
@@ -291,7 +308,7 @@ const TaskWrapper = styled.div`
291
308
  const PinButton = styled.button<{ isPinned: boolean }>`
292
309
  position: absolute;
293
310
  top: 8px;
294
- right: 8px;
311
+ right: 32px;
295
312
  background: rgba(0, 0, 0, 0.7);
296
313
  color: ${props => props.isPinned ? uiColors.yellow : uiColors.lightGray};
297
314
  border: 1px solid ${props => props.isPinned ? uiColors.yellow : 'transparent'};