Skip to content

Commit 9f77d68

Browse files
Merge pull request #105 from cuappdev/melissa/profile-page
Profile Page Implementation
2 parents 7184790 + 422ef3e commit 9f77d68

22 files changed

Lines changed: 778 additions & 302 deletions

app/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ android {
4848
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
4949
buildConfigField("String", "GOOGLE_AUTH_CLIENT_ID", secretsProperties["GOOGLE_AUTH_CLIENT_ID"])
5050
buildConfigField("String", "BACKEND_URL", secretsProperties['PROD_ENDPOINT'])
51-
buildConfigField("boolean", "ONBOARDING_FLAG", "false")
52-
buildConfigField("boolean", "CHECK_IN_FLAG", "false")
51+
buildConfigField("boolean", "ONBOARDING_FLAG", "true")
52+
buildConfigField("boolean", "CHECK_IN_FLAG", "true")
5353
}
5454
debug {
5555
buildConfigField("String", "BACKEND_URL", secretsProperties['DEV_ENDPOINT'])
@@ -58,8 +58,8 @@ android {
5858
"GOOGLE_AUTH_CLIENT_ID", secretsProperties["GOOGLE_AUTH_CLIENT_ID"]
5959
)
6060
signingConfig signingConfigs.debug
61-
buildConfigField("boolean", "ONBOARDING_FLAG", "false")
62-
buildConfigField("boolean", "CHECK_IN_FLAG", "false")
61+
buildConfigField("boolean", "ONBOARDING_FLAG", "true")
62+
buildConfigField("boolean", "CHECK_IN_FLAG", "true")
6363
}
6464
}
6565
compileOptions {

app/src/main/graphql/User.graphql

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
fragment userFields on User {
22
id
33
email
4-
name
54
netId
5+
name
6+
encodedImage
7+
activeStreak
8+
maxStreak
9+
streakStart
10+
workoutGoal
11+
lastGoalChange
12+
lastStreak
13+
totalGymDays
14+
workoutHistory {
15+
...workoutFields
16+
}
617
}
718

819
fragment workoutFields on Workout {
920
id
1021
workoutTime
1122
userId
23+
facilityId
24+
gymName
1225
}
1326

1427
mutation CreateUser($email: String!, $name: String!, $netId: String!) {
@@ -33,14 +46,14 @@ query getUserByNetId($netId: String!) {
3346
}
3447
}
3548

36-
mutation SetWorkoutGoals($id: Int!, $workoutGoal: Int!) {
37-
setWorkoutGoals(userId: $id, workoutGoal: $workoutGoal) {
49+
mutation SetWorkoutGoals($userId: Int!, $workoutGoal: Int!) {
50+
setWorkoutGoals(userId: $userId, workoutGoal: $workoutGoal) {
3851
...userFields
3952
}
4053
}
4154

42-
mutation LogWorkout($facilityId: Int!, $workoutTime: DateTime!, $id: Int!) {
43-
logWorkout(facilityId: $facilityId, userId: $id, workoutTime: $workoutTime) {
55+
mutation LogWorkout($facilityId: Int!, $workoutTime: DateTime!, $userId: Int!) {
56+
logWorkout(facilityId: $facilityId, userId: $userId, workoutTime: $workoutTime) {
4457
...workoutFields
4558
}
4659
}

app/src/main/graphql/schema.graphqls

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -378,9 +378,11 @@ type User {
378378
totalGymDays: Int!
379379

380380
"""
381-
The start date of the most recent active streak, up until the current date.
381+
The start datetime of the most recent active streak (midnight of the day in local timezone), up until the current date.
382382
"""
383-
streakStart: Date
383+
streakStart: DateTime
384+
385+
workoutHistory: [Workout]
384386
}
385387

386388
type Giveaway {
@@ -421,13 +423,6 @@ type Friendship {
421423
friend: User
422424
}
423425

424-
"""
425-
The `Date` scalar type represents a Date
426-
value as specified by
427-
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
428-
"""
429-
scalar Date
430-
431426
type Workout {
432427
id: ID!
433428

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.cornellappdev.uplift.data.models
2+
3+
import android.net.Uri
4+
5+
6+
data class ProfileData(
7+
val name: String,
8+
val netId: String,
9+
val encodedImage: String?,
10+
val totalGymDays: Int,
11+
val activeStreak: Int,
12+
val maxStreak: Int,
13+
val streakStart: String?,
14+
val workoutGoal: Int,
15+
val workouts: List<WorkoutDomain>,
16+
val weeklyWorkoutDays: List<String>
17+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package com.cornellappdev.uplift.data.models
22
import kotlinx.serialization.Serializable
33

4+
45
@Serializable
56
data class UserInfo(
67
val id: String,
78
val email: String,
89
val name: String,
910
val netId: String,
11+
val encodedImage: String?,
12+
val activeStreak: Int?,
13+
val maxStreak: Int?,
14+
val streakStart: String?,
15+
val workoutGoal: Int?,
16+
val totalGymDays: Int
1017
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.cornellappdev.uplift.data.models
2+
3+
data class WorkoutDomain(
4+
val gymName: String,
5+
val timestamp: Long
6+
)

app/src/main/java/com/cornellappdev/uplift/data/repositories/AuthInterceptor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class AuthInterceptor @Inject constructor(
1111
) : Interceptor {
1212
override fun intercept(chain: Interceptor.Chain): Response {
1313
val token = tokenManager.getAccessToken()
14+
android.util.Log.d("AuthInterceptor", "token present = ${token != null}")
1415
val request = chain.request().newBuilder().apply {
1516
if (token != null) {
1617
addHeader("Authorization", "Bearer $token")

app/src/main/java/com/cornellappdev/uplift/data/repositories/CheckInRepository.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,25 @@ class CheckInRepository @Inject constructor(
150150
* Logs a completed workout to the backend. Returns true if the mutation succeeded, false otherwise.
151151
*/
152152
suspend fun logWorkoutFromCheckIn(gymId: Int): Boolean {
153-
val userId = userInfoRepository.getUserIdFromDataStore()?.toIntOrNull() ?: return false
153+
val userIdString = userInfoRepository.getUserIdFromDataStore()
154+
val userId = userIdString?.toIntOrNull()
155+
156+
if (userId == null) {
157+
Log.e("CheckInRepository", "Missing or invalid userId in DataStore: $userIdString")
158+
return false
159+
}
160+
154161
val time = Instant.now().toString()
155162

156163
return try {
157164
val response = apolloClient
158-
.mutation(LogWorkoutMutation(facilityId = gymId, workoutTime = time, id = userId ))
165+
.mutation(
166+
LogWorkoutMutation(
167+
facilityId = gymId,
168+
workoutTime = time,
169+
userId = userId
170+
)
171+
)
159172
.execute()
160173

161174
val ok = response.data?.logWorkout?.workoutFields != null && !response.hasErrors()

app/src/main/java/com/cornellappdev/uplift/data/repositories/DatastoreRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ object PreferencesKeys {
2626
val NETID = stringPreferencesKey("netId")
2727
val EMAIL = stringPreferencesKey("email")
2828
val GOAL = intPreferencesKey("workoutGoal")
29-
val SKIP = booleanPreferencesKey("skip")
29+
val GOAL_SETTING_SKIPPED = booleanPreferencesKey("goalSkip")
3030
val FCM_TOKEN = stringPreferencesKey("fcmToken")
3131
val DECLINED_NOTIFICATION_PERMISSION =
3232
booleanPreferencesKey("declinedNotificationPermission")
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.cornellappdev.uplift.data.repositories
2+
3+
import android.util.Log
4+
import com.apollographql.apollo.ApolloClient
5+
import com.cornellappdev.uplift.GetUserByNetIdQuery
6+
import com.cornellappdev.uplift.GetWeeklyWorkoutDaysQuery
7+
import com.cornellappdev.uplift.SetWorkoutGoalsMutation
8+
import com.cornellappdev.uplift.data.models.ProfileData
9+
import com.cornellappdev.uplift.data.models.WorkoutDomain
10+
import java.time.Instant
11+
import javax.inject.Inject
12+
import javax.inject.Singleton
13+
14+
15+
@Singleton
16+
class ProfileRepository @Inject constructor(
17+
private val userInfoRepository: UserInfoRepository,
18+
private val apolloClient: ApolloClient
19+
) {
20+
suspend fun getProfile(): Result<ProfileData> = runCatching {
21+
val netId = userInfoRepository.getNetIdFromDataStore()
22+
?: throw IllegalStateException("NetId missing")
23+
24+
val userResponse = apolloClient.query(
25+
GetUserByNetIdQuery(netId)
26+
).execute()
27+
28+
if (userResponse.hasErrors()) {
29+
Log.e("ProfileRepo", "User query errors: ${userResponse.errors}")
30+
throw IllegalStateException("User query failed")
31+
}
32+
33+
val user = userResponse.data?.getUserByNetId?.firstOrNull()?.userFields
34+
?: throw IllegalStateException("User not found")
35+
36+
val userId = user.id.toIntOrNull()
37+
?: throw IllegalStateException("Invalid user ID: ${user.id}")
38+
39+
val weeklyResponse = apolloClient.query(GetWeeklyWorkoutDaysQuery(userId)).execute()
40+
41+
if (weeklyResponse.hasErrors()) {
42+
throw IllegalStateException("Weekly workout days query failed: ${weeklyResponse.errors}")
43+
}
44+
45+
val workouts = user.workoutHistory?.filterNotNull().orEmpty()
46+
47+
val workoutDomain = workouts.map {
48+
WorkoutDomain(
49+
gymName = it.workoutFields.gymName,
50+
timestamp = Instant.parse(it.workoutFields.workoutTime.toString())
51+
.toEpochMilli()
52+
)
53+
}
54+
55+
val weeklyDays = weeklyResponse.data?.getWeeklyWorkoutDays?.filterNotNull().orEmpty()
56+
57+
ProfileData(
58+
name = user.name,
59+
netId = user.netId,
60+
encodedImage = user.encodedImage,
61+
totalGymDays = user.totalGymDays,
62+
activeStreak = user.activeStreak,
63+
maxStreak = user.maxStreak,
64+
streakStart = user.streakStart?.toString(),
65+
workoutGoal = user.workoutGoal ?: 0,
66+
workouts = workoutDomain,
67+
weeklyWorkoutDays = weeklyDays
68+
)
69+
}.onFailure { e ->
70+
Log.e("ProfileRepo", "Failed to load profile", e)
71+
}
72+
73+
suspend fun setWorkoutGoal(goal: Int): Result<Unit> = runCatching {
74+
val userId = userInfoRepository.getUserIdFromDataStore()?.toIntOrNull()
75+
?: throw IllegalStateException("Missing user ID")
76+
77+
val response = apolloClient
78+
.mutation(
79+
SetWorkoutGoalsMutation(
80+
userId = userId,
81+
workoutGoal = goal
82+
)
83+
)
84+
.execute()
85+
86+
if (response.hasErrors()) {
87+
throw IllegalStateException("Goal update failed")
88+
}
89+
}.onFailure { e ->
90+
Log.e("ProfileRepo", "Failed to update workout goal", e)
91+
}
92+
}

0 commit comments

Comments
 (0)