Skip to content

Commit bbe9f5b

Browse files
authored
Merge pull request #79 from RealTeamRocket/76-endpoint-for-rocketpoints
76 endpoint for rocketpoints
2 parents 81b59e1 + aca9651 commit bbe9f5b

7 files changed

Lines changed: 216 additions & 65 deletions

File tree

mobile_app/lib/pages/run_page.dart

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
22
import '/utils/utils.dart';
33
import '/widgets/widgets.dart';
44
import '/constants/constants.dart';
5+
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
6+
7+
import '../utils/backend_api/rocketpoints_api.dart';
58

69
class RunPage extends StatefulWidget {
710
const RunPage({super.key, required this.title});
@@ -15,6 +18,7 @@ class _RunPageState extends State<RunPage> {
1518
final int dailyGoal = 10000;
1619
int currentSteps = 0;
1720
String selectedButton = 'Steps';
21+
int? rocketPoints;
1822

1923
late PedometerService _pedometerService;
2024

@@ -30,13 +34,30 @@ class _RunPageState extends State<RunPage> {
3034

3135
_pedometerService.onError = (msg) {
3236
if (mounted) {
33-
ScaffoldMessenger.of(context).showSnackBar(
34-
SnackBar(content: Text(msg)),
35-
);
37+
ScaffoldMessenger.of(
38+
context,
39+
).showSnackBar(SnackBar(content: Text(msg)));
3640
}
3741
};
3842

3943
_pedometerService.init();
44+
45+
_loadRocketPoints();
46+
}
47+
48+
Future<void> _loadRocketPoints() async {
49+
try {
50+
final jwt = await FlutterSecureStorage().read(key: 'jwt_token');
51+
if (jwt == null) return;
52+
final response = await RocketPointsApi.fetchRocketPoints(jwt);
53+
setState(() {
54+
rocketPoints = response.rocketPoints;
55+
});
56+
} catch (e) {
57+
setState(() {
58+
rocketPoints = null;
59+
});
60+
}
4061
}
4162

4263
void _onButtonPressed(String button) {
@@ -53,10 +74,64 @@ class _RunPageState extends State<RunPage> {
5374
mainAxisAlignment: MainAxisAlignment.center,
5475
children: <Widget>[
5576
if (selectedButton == 'Steps') ...[
56-
StepCounterWidget(
57-
currentSteps: currentSteps,
58-
dailyGoal: dailyGoal,
77+
/// RocketPoints-Card
78+
Column(
79+
children: [
80+
const SizedBox(height: 20.0),
81+
SizedBox(
82+
height: 100,
83+
child: Column(
84+
mainAxisAlignment: MainAxisAlignment.center,
85+
children: [
86+
Center(
87+
child: IntrinsicWidth(
88+
child: Container(
89+
decoration: BoxDecoration(
90+
color: ColorConstants.secoundaryColor,
91+
borderRadius: BorderRadius.circular(16.0),
92+
border: Border.all(
93+
color: ColorConstants.purpleColor.withValues(
94+
alpha: 0.3,
95+
),
96+
width: 2.5,
97+
),
98+
boxShadow: [
99+
BoxShadow(
100+
color: ColorConstants.secoundaryColor
101+
.withValues(alpha: 0.2),
102+
blurRadius: 6.0,
103+
offset: const Offset(0, 3),
104+
),
105+
],
106+
),
107+
padding: const EdgeInsets.symmetric(
108+
vertical: 16.0,
109+
horizontal: 16.0,
110+
),
111+
child: Center(
112+
child: Text(
113+
rocketPoints != null
114+
? '🚀 $rocketPoints RPs'
115+
: '🚀 ... RPs',
116+
style: const TextStyle(
117+
fontSize: 28.0,
118+
fontWeight: FontWeight.bold,
119+
color: ColorConstants.greenColor,
120+
),
121+
),
122+
),
123+
),
124+
),
125+
),
126+
],
127+
),
128+
),
129+
const SizedBox(height: 12.0),
130+
],
59131
),
132+
133+
/// Progressbar
134+
StepCounterWidget(currentSteps: currentSteps, dailyGoal: dailyGoal),
60135
const SizedBox(height: 20.0),
61136
] else if (selectedButton == 'Race') ...[
62137
Text(
@@ -69,12 +144,14 @@ class _RunPageState extends State<RunPage> {
69144
),
70145
const SizedBox(height: 20.0),
71146
],
147+
148+
/// Buttons
72149
ButtonsWidget(
73150
selectedButton: selectedButton,
74151
onButtonPressed: _onButtonPressed,
75152
),
76153
],
77-
)
154+
),
78155
);
79156
}
80157
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'dart:convert';
2+
import 'base_api.dart';
3+
4+
class RocketPointsResponse {
5+
final int rocketPoints;
6+
7+
const RocketPointsResponse({required this.rocketPoints});
8+
9+
factory RocketPointsResponse.fromJson(Map<String, dynamic> json) {
10+
return RocketPointsResponse(rocketPoints: json['rocket_points'] ?? 0);
11+
}
12+
13+
@override
14+
String toString() => "rocketPoints: $rocketPoints";
15+
}
16+
17+
class RocketPointsApi {
18+
static Future<RocketPointsResponse> fetchRocketPoints(String jwt) async {
19+
final response = await BaseApi.get(
20+
'/api/v1/protected/user/rocketpoints',
21+
headers: {'Authorization': 'Bearer $jwt'},
22+
);
23+
24+
if (response.statusCode != 200) {
25+
throw Exception('Failed to fetch rocket points: ${response.statusCode}');
26+
} else {
27+
return RocketPointsResponse.fromJson(
28+
jsonDecode(response.body) as Map<String, dynamic>,
29+
);
30+
}
31+
}
32+
}

rocket-backend/integration-tests/mocks/database_mock.go

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,40 @@ import (
1010
)
1111

1212
type MockDB struct {
13-
ExecuteRawSQLFunc func(query string) (sql.Result, error)
14-
QueryRowFunc func(query string, args ...interface{}) *sql.Row
15-
HealthFunc func() map[string]string
16-
CloseFunc func() error
17-
SaveCredentialsFunc func(creds types.Credentials) error
18-
GetUserByEmailFunc func(email string) (types.Credentials, error)
19-
CheckEmailFunc func(email string) error
20-
SaveUserProfileFunc func(user types.User) error
21-
GetUserByIDFunc func(userID uuid.UUID) (types.User, error)
22-
UpdateRocketPointsFunc func(userID uuid.UUID, rocketPoints int) error
23-
GetUserIDByNameFunc func(name string) (uuid.UUID, error)
24-
GetTopUsersFunc func(limit int) ([]types.User, error)
25-
GetAllUsersFunc func() ([]types.User, error) // Missing method
26-
UpdateDailyStepsFunc func(userID uuid.UUID, steps int) error
27-
GetUserStatisticsFunc func(userID uuid.UUID) ([]types.StepStatistic, error)
28-
GetSettingsByUserIDFunc func(userID uuid.UUID) (*types.Settings, error)
29-
CreateSettingsFunc func(settings types.Settings) error
30-
UpdateSettingsStepGoalFunc func(userID uuid.UUID, stepGoal int) error
31-
UpdateSettingsImageFunc func(userID uuid.UUID, imageID uuid.UUID) error
32-
UpdateStepGoalFunc func(userID uuid.UUID, stepGoal int) error
33-
UpdateImageFunc func(userID uuid.UUID, imageID uuid.UUID) error
34-
SaveImageFunc func(filename string, data []byte) (uuid.UUID, error)
35-
GetUserImageFunc func(userID uuid.UUID) (*types.UserImage, error)
36-
GetAllChallengesFunc func() ([]types.Challenge, error)
37-
AssignChallengesToUserFunc func(userID uuid.UUID, challenges []types.Challenge) error
38-
GetUserDailyChallengesFunc func(userID uuid.UUID) ([]types.Challenge, error)
39-
ResetDailyChallengesFunc func() error
40-
InsertChallengeFunc func(challenge types.Challenge) error
41-
CompleteChallengeFunc func(userID uuid.UUID, dto types.CompleteChallengesDTO) error
42-
IsNewDayForUserFunc func(userID uuid.UUID) (bool, error)
13+
ExecuteRawSQLFunc func(query string) (sql.Result, error)
14+
QueryRowFunc func(query string, args ...interface{}) *sql.Row
15+
HealthFunc func() map[string]string
16+
CloseFunc func() error
17+
SaveCredentialsFunc func(creds types.Credentials) error
18+
GetUserByEmailFunc func(email string) (types.Credentials, error)
19+
CheckEmailFunc func(email string) error
20+
SaveUserProfileFunc func(user types.User) error
21+
GetUserByIDFunc func(userID uuid.UUID) (types.User, error)
22+
UpdateRocketPointsFunc func(userID uuid.UUID, rocketPoints int) error
23+
GetRocketPointsByUserIDFunc func(userID uuid.UUID) (int, error)
24+
GetUserIDByNameFunc func(name string) (uuid.UUID, error)
25+
GetTopUsersFunc func(limit int) ([]types.User, error)
26+
GetAllUsersFunc func() ([]types.User, error) // Missing method
27+
UpdateDailyStepsFunc func(userID uuid.UUID, steps int) error
28+
GetUserStatisticsFunc func(userID uuid.UUID) ([]types.StepStatistic, error)
29+
GetSettingsByUserIDFunc func(userID uuid.UUID) (*types.Settings, error)
30+
CreateSettingsFunc func(settings types.Settings) error
31+
UpdateSettingsStepGoalFunc func(userID uuid.UUID, stepGoal int) error
32+
UpdateSettingsImageFunc func(userID uuid.UUID, imageID uuid.UUID) error
33+
UpdateStepGoalFunc func(userID uuid.UUID, stepGoal int) error
34+
UpdateImageFunc func(userID uuid.UUID, imageID uuid.UUID) error
35+
SaveImageFunc func(filename string, data []byte) (uuid.UUID, error)
36+
GetUserImageFunc func(userID uuid.UUID) (*types.UserImage, error)
37+
GetAllChallengesFunc func() ([]types.Challenge, error)
38+
AssignChallengesToUserFunc func(userID uuid.UUID, challenges []types.Challenge) error
39+
GetUserDailyChallengesFunc func(userID uuid.UUID) ([]types.Challenge, error)
40+
ResetDailyChallengesFunc func() error
41+
InsertChallengeFunc func(challenge types.Challenge) error
42+
CompleteChallengeFunc func(userID uuid.UUID, dto types.CompleteChallengesDTO) error
43+
IsNewDayForUserFunc func(userID uuid.UUID) (bool, error)
4344
CleanUpChallengesForUserFunc func(userID uuid.UUID) error
44-
AddFriendFunc func(userID, friendID uuid.UUID) error
45-
GetFriendsFunc func(userID uuid.UUID) ([]types.User, error)
45+
AddFriendFunc func(userID, friendID uuid.UUID) error
46+
GetFriendsFunc func(userID uuid.UUID) ([]types.User, error)
4647
GetFriendsRankedByPointsFunc func(userID uuid.UUID) ([]types.User, error)
4748
DeleteFriendFunc func(userID, friendID uuid.UUID) error
4849
}
@@ -117,6 +118,13 @@ func (m *MockDB) UpdateRocketPoints(userID uuid.UUID, rocketPoints int) error {
117118
return nil
118119
}
119120

121+
func (m *MockDB) GetRocketPointsByUserID(userID uuid.UUID) (int, error) {
122+
if m.GetRocketPointsByUserIDFunc != nil {
123+
return m.GetRocketPointsByUserIDFunc(userID)
124+
}
125+
return 0, nil
126+
}
127+
120128
func (m *MockDB) GetUserIDByName(name string) (uuid.UUID, error) {
121129
if m.GetUserIDByNameFunc != nil {
122130
return m.GetUserIDByNameFunc(name)

rocket-backend/internal/database/database.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Service interface {
3838
SaveUserProfile(user types.User) error
3939
GetUserByID(userID uuid.UUID) (types.User, error)
4040
UpdateRocketPoints(userID uuid.UUID, rocketPoints int) error
41+
GetRocketPointsByUserID(userID uuid.UUID) (int, error)
4142
GetUserIDByName(name string) (uuid.UUID, error)
4243
GetTopUsers(limit int) ([]types.User, error)
4344
GetAllUsers() ([]types.User, error)

rocket-backend/internal/database/user_table.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,24 @@ func (s *service) UpdateRocketPoints(userID uuid.UUID, rocketPoints int) error {
3939
return nil
4040
}
4141

42+
func (s *service) GetRocketPointsByUserID(userID uuid.UUID) (int, error) {
43+
var rocketPoints int
44+
query := `SELECT rocketpoints FROM users WHERE id = $1`
45+
err := s.db.QueryRow(query, userID).Scan(&rocketPoints)
46+
if err != nil {
47+
return 0, fmt.Errorf("%w: %v", custom_error.ErrFailedToRetrieveData, err)
48+
}
49+
return rocketPoints, nil
50+
}
51+
4252
func (s *service) GetUserIDByName(name string) (uuid.UUID, error) {
43-
var userID uuid.UUID
44-
query := `SELECT id FROM users WHERE username = $1`
45-
err := s.db.QueryRow(query, name).Scan(&userID)
46-
if err != nil {
47-
return uuid.Nil, fmt.Errorf("failed to get user ID by name: %w", err)
48-
}
49-
return userID, nil
53+
var userID uuid.UUID
54+
query := `SELECT id FROM users WHERE username = $1`
55+
err := s.db.QueryRow(query, name).Scan(&userID)
56+
if err != nil {
57+
return uuid.Nil, fmt.Errorf("failed to get user ID by name: %w", err)
58+
}
59+
return userID, nil
5060
}
5161

5262
func (s *service) GetTopUsers(limit int) ([]types.User, error) {

rocket-backend/internal/server/protected_handlers.go

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -206,25 +206,25 @@ func (s *Server) GetAllFriends(c *gin.Context) {
206206
return
207207
}
208208

209-
var friendsWithImages []types.UserWithImageDTO
210-
for _, fr := range friends {
211-
var f types.UserWithImageDTO
212-
f.ID = fr.ID
213-
f.Username = fr.Username
214-
f.Email = fr.Email
215-
f.RocketPoints = fr.RocketPoints
216-
217-
userImage, imgErr := s.db.GetUserImage(fr.ID)
218-
if imgErr != nil {
219-
logger.Warn("Failed to fetch image for friend %s: %v\n", fr.ID, imgErr)
220-
f.ImageName = ""
221-
f.ImageData = ""
222-
} else if userImage != nil {
223-
f.ImageName = userImage.Name
224-
f.ImageData = base64.StdEncoding.EncodeToString(userImage.Data)
225-
}
226-
friendsWithImages = append(friendsWithImages, f)
227-
}
209+
var friendsWithImages []types.UserWithImageDTO
210+
for _, fr := range friends {
211+
var f types.UserWithImageDTO
212+
f.ID = fr.ID
213+
f.Username = fr.Username
214+
f.Email = fr.Email
215+
f.RocketPoints = fr.RocketPoints
216+
217+
userImage, imgErr := s.db.GetUserImage(fr.ID)
218+
if imgErr != nil {
219+
logger.Warn("Failed to fetch image for friend %s: %v\n", fr.ID, imgErr)
220+
f.ImageName = ""
221+
f.ImageData = ""
222+
} else if userImage != nil {
223+
f.ImageName = userImage.Name
224+
f.ImageData = base64.StdEncoding.EncodeToString(userImage.Data)
225+
}
226+
friendsWithImages = append(friendsWithImages, f)
227+
}
228228

229229
c.JSON(http.StatusOK, friendsWithImages)
230230
}
@@ -532,3 +532,25 @@ func (s *Server) UpdateImage(c *gin.Context) {
532532

533533
c.JSON(http.StatusOK, gin.H{"message": "Image updated successfully"})
534534
}
535+
536+
func (s *Server) GetRocketPoints(c *gin.Context) {
537+
userID, exists := c.Get("userID")
538+
if !exists {
539+
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
540+
return
541+
}
542+
543+
userUUID, err := uuid.Parse(userID.(string))
544+
if err != nil {
545+
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID format"})
546+
return
547+
}
548+
549+
rocketPoints, err := s.db.GetRocketPointsByUserID(userUUID)
550+
if err != nil {
551+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve rocket points"})
552+
return
553+
}
554+
555+
c.JSON(http.StatusOK, gin.H{"rocket_points": rocketPoints})
556+
}

rocket-backend/internal/server/routes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func (s *Server) RegisterRoutes() http.Handler {
3636

3737
protected.POST("user/statistics", s.getUserStatistics)
3838
protected.POST("user/image", s.GetUserImage)
39+
protected.GET("user/rocketpoints", s.GetRocketPoints)
3940

4041
protected.GET("/challenges/new", s.GetDailyChallenges)
4142
protected.POST("/challenges/complete", s.CompleteChallenge)

0 commit comments

Comments
 (0)