Skip to content

Commit c5ed62c

Browse files
authored
Merge pull request #56 from GenerateNU/fix-inconsistent-leveling-exp
Fix inconsistent leveling exp
2 parents 3e1605a + 54cfae7 commit c5ed62c

File tree

8 files changed

+1994
-606
lines changed

8 files changed

+1994
-606
lines changed

frontend/app/(tabs)/profile.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import React from 'react';
21
import {
32
View,
43
Text,

frontend/app/(tabs)/rank.tsx

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { volunteerService } from '@/services/volunteerService';
1616
import { useVolunteerProfileImage } from '@/hooks/useVolunteerProfileImage';
1717
import { useAuth } from '@/context/AuthContext';
1818
import { Volunteer } from '@/types/api/volunteer';
19+
import { useProfile } from '@/hooks/useProfile';
1920

2021
interface LeaderboardEntry {
2122
rank: number;
@@ -26,26 +27,6 @@ interface LeaderboardEntry {
2627
volunteer: Volunteer;
2728
}
2829

29-
// Calculate level from experience (same logic as useProfile)
30-
function calculateLevel(experience: number): number {
31-
if (experience < 100) return 1;
32-
33-
let level = 1;
34-
let totalXPNeeded = 0;
35-
36-
while (totalXPNeeded <= experience) {
37-
const xpForNextLevel = level * 100 + (level - 1) * 50;
38-
totalXPNeeded += xpForNextLevel;
39-
if (totalXPNeeded <= experience) {
40-
level++;
41-
} else {
42-
break;
43-
}
44-
}
45-
46-
return level;
47-
}
48-
4930
function getVolunteerName(volunteer: Volunteer): string {
5031
if (volunteer.preferredName) {
5132
return volunteer.preferredName;
@@ -127,6 +108,7 @@ function Avatar({ volunteerId, size }: AvatarProps) {
127108
export default function LeaderboardScreen() {
128109
const { user } = useAuth();
129110
const router = useRouter();
111+
const profile = useProfile();
130112
const [leaderboardData, setLeaderboardData] = useState<LeaderboardEntry[]>(
131113
[]
132114
);
@@ -160,8 +142,7 @@ export default function LeaderboardScreen() {
160142
rank: index + 1,
161143
name: getVolunteerName(volunteer),
162144
experience: volunteer.experience,
163-
level:
164-
volunteer.currentLevel ?? calculateLevel(volunteer.experience),
145+
level: volunteer.currentLevel,
165146
isCurrentUser: volunteerData?.id === volunteer.id,
166147
volunteer,
167148
})
@@ -177,7 +158,7 @@ export default function LeaderboardScreen() {
177158
};
178159

179160
fetchLeaderboard();
180-
}, [user?.entityId]);
161+
}, [user?.entityId, profile.volunteer?.experience]);
181162

182163
if (loading) {
183164
return (

frontend/app/(tabs)/shop.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ import { Fonts } from '@/constants/Fonts';
1515
import { Colors } from '@/constants/Colors';
1616
import { useRouter } from 'expo-router';
1717
import { useAuth } from '@/context/AuthContext';
18+
import { useProfile } from '@/hooks/useProfile';
1819
import { itemService } from '@/services/itemService';
20+
import { volunteerService } from '@/services/volunteerService';
1921
import { LoadingScreen } from '@/components/LoadingScreen';
2022
import SearchInputWithFilter from '@/components/SearchInputWithFilter';
2123
import ItemFilterDrawer from '@/components/drawers/ItemFilterDrawer';
2224
import { ShopItem } from '@/types/api/item';
25+
import { Volunteer } from '@/types/api/volunteer';
2326

2427
export interface ItemFilters {
2528
priceRange: { min: number; max: number };
@@ -32,13 +35,17 @@ export default function StoreScreen() {
3235
const [loading, setLoading] = useState(false);
3336
const [refreshing, setRefreshing] = useState(false);
3437
const [drawerOpen, setDrawerOpen] = useState(false);
38+
const profile = useProfile();
3539
const [filters, setFilters] = useState<ItemFilters>({
3640
priceRange: { min: 0, max: 10000 },
3741
category: '',
3842
});
43+
const [currentVolunteer, setCurrentVolunteer] = useState<Volunteer | null>(
44+
null
45+
);
3946

4047
const router = useRouter();
41-
const { volunteer, token } = useAuth();
48+
const { user, token } = useAuth();
4249

4350
const loadItems = useCallback(async () => {
4451
try {
@@ -71,8 +78,22 @@ export default function StoreScreen() {
7178
}, [token]);
7279

7380
useEffect(() => {
74-
loadItems();
75-
}, [loadItems]);
81+
// Fetch current volunteer if user is available (same approach as profile page)
82+
const fetchVolunteer = async () => {
83+
let volunteerData: Volunteer | null = null;
84+
if (user?.entityId) {
85+
try {
86+
volunteerData = await volunteerService.getSelf();
87+
setCurrentVolunteer(volunteerData);
88+
} catch (err) {
89+
console.error('Error fetching current volunteer:', err);
90+
}
91+
}
92+
93+
await loadItems();
94+
};
95+
fetchVolunteer();
96+
}, [loadItems, profile.volunteer?.experience]);
7697

7798
const filteredItems = useMemo(() => {
7899
return items.filter(item => {
@@ -113,6 +134,12 @@ export default function StoreScreen() {
113134
}
114135
}, [loadItems]);
115136

137+
// useFocusEffect(
138+
// useCallback(() => {
139+
// refreshProfile(); // pulls latest XP, coins, level
140+
// }, [])
141+
// );
142+
116143
if (loading) {
117144
return <LoadingScreen text="Loading items..." />;
118145
}
@@ -156,7 +183,7 @@ export default function StoreScreen() {
156183
fontFamily: Fonts.medium_500,
157184
}}
158185
>
159-
Level {volunteer?.currentLevel ?? 0}
186+
Level {currentVolunteer?.currentLevel ?? 0}
160187
</ThemedText>
161188

162189
<ThemedText
@@ -167,7 +194,7 @@ export default function StoreScreen() {
167194
fontFamily: Fonts.medium_500,
168195
}}
169196
>
170-
{volunteer?.coins ?? 0} coins
197+
{currentVolunteer?.coins ?? 0} coins
171198
</ThemedText>
172199

173200
<TouchableOpacity

frontend/hooks/useProfile.ts

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,58 +8,7 @@ import { Volunteer } from '@/types/api/volunteer';
88
import { useCurrentVolunteer } from '@/hooks/useCurrentVolunteer';
99
import { useUpcomingEvents } from '@/hooks/useUpcomingEvents';
1010

11-
// temporary logic idk how we're calculating level lol
12-
function calculateLevel(experience: number): number {
13-
if (experience < 100) return 1;
14-
15-
let level = 1;
16-
let totalXPNeeded = 0;
17-
18-
while (totalXPNeeded <= experience) {
19-
const xpForNextLevel = level * 100 + (level - 1) * 50;
20-
totalXPNeeded += xpForNextLevel;
21-
if (totalXPNeeded <= experience) {
22-
level++;
23-
} else {
24-
break;
25-
}
26-
}
27-
28-
return level;
29-
}
30-
31-
function getXPForLevel(level: number): number {
32-
let totalXP = 0;
33-
for (let i = 1; i < level; i++) {
34-
totalXP += i * 100 + (i - 1) * 50;
35-
}
36-
return totalXP;
37-
}
38-
39-
function getXPForNextLevel(currentLevel: number): number {
40-
return currentLevel * 100 + (currentLevel - 1) * 50;
41-
}
42-
43-
function calculateLevelProgress(experience: number): {
44-
level: number;
45-
progress: number; // 0-100
46-
currentLevelXP: number;
47-
nextLevelXP: number;
48-
} {
49-
const level = calculateLevel(experience);
50-
const xpForCurrentLevel = getXPForLevel(level);
51-
const xpForNextLevel = getXPForNextLevel(level);
52-
const currentLevelXP = experience - xpForCurrentLevel;
53-
const progress = (currentLevelXP / xpForNextLevel) * 100;
54-
55-
return {
56-
level,
57-
progress: Math.min(Math.max(progress, 0), 100),
58-
currentLevelXP,
59-
nextLevelXP: xpForNextLevel,
60-
};
61-
}
62-
11+
// TODO: abstract to backend asw
6312
function calculateTotalHours(completedEvents: Event[]): number {
6413
return completedEvents.reduce((sum, event) => {
6514
const start = new Date(event.startDateTime);
@@ -113,15 +62,14 @@ export function useProfile() {
11362
return;
11463
}
11564

116-
const levelData = calculateLevelProgress(v.experience);
65+
const levelProgress = await profileService.getLevelProgress();
11766
const totalHours = calculateTotalHours(pastEventsData);
11867

11968
const stats = {
12069
totalHours,
121-
level: levelData.level,
122-
levelProgress: levelData.progress,
70+
level: v.currentLevel,
71+
levelProgress: levelProgress ?? 0, // meh
12372
experiencePoints: v.experience,
124-
nextLevelXP: levelData.nextLevelXP,
12573
};
12674

12775
setProfileData({

0 commit comments

Comments
 (0)