-
Notifications
You must be signed in to change notification settings - Fork 0
Profile Page Implementation #105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7bb9c0a
7aeedf9
200d397
fc3d451
79e36f9
07f6cab
2af071e
80817e7
97786c8
b95621c
422ef3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.cornellappdev.uplift.data.models | ||
|
|
||
| import android.net.Uri | ||
|
|
||
|
|
||
| data class ProfileData( | ||
| val name: String, | ||
| val netId: String, | ||
| val encodedImage: String?, | ||
| val totalGymDays: Int, | ||
| val activeStreak: Int, | ||
| val maxStreak: Int, | ||
| val streakStart: String?, | ||
| val workoutGoal: Int, | ||
| val workouts: List<WorkoutDomain>, | ||
| val weeklyWorkoutDays: List<String> | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,17 @@ | ||
| package com.cornellappdev.uplift.data.models | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
|
|
||
| @Serializable | ||
| data class UserInfo( | ||
| val id: String, | ||
| val email: String, | ||
| val name: String, | ||
| val netId: String, | ||
| val encodedImage: String?, | ||
| val activeStreak: Int?, | ||
| val maxStreak: Int?, | ||
| val streakStart: String?, | ||
| val workoutGoal: Int?, | ||
| val totalGymDays: Int | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.cornellappdev.uplift.data.models | ||
|
|
||
| data class WorkoutDomain( | ||
| val gymName: String, | ||
| val timestamp: Long | ||
| ) |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,6 +11,7 @@ class AuthInterceptor @Inject constructor( | |||||||||||
| ) : Interceptor { | ||||||||||||
| override fun intercept(chain: Interceptor.Chain): Response { | ||||||||||||
| val token = tokenManager.getAccessToken() | ||||||||||||
| android.util.Log.d("AuthInterceptor", "token present = ${token != null}") | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove debug log statement before release. This debug log exposes authentication state information ( 🛡️ Proposed fix- android.util.Log.d("AuthInterceptor", "token present = ${token != null}")
+ // Remove or guard with BuildConfig.DEBUG if needed for debugging:
+ // if (BuildConfig.DEBUG) {
+ // android.util.Log.d("AuthInterceptor", "token present = ${token != null}")
+ // }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| val request = chain.request().newBuilder().apply { | ||||||||||||
| if (token != null) { | ||||||||||||
| addHeader("Authorization", "Bearer $token") | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,12 +150,25 @@ class CheckInRepository @Inject constructor( | |
| * Logs a completed workout to the backend. Returns true if the mutation succeeded, false otherwise. | ||
| */ | ||
| suspend fun logWorkoutFromCheckIn(gymId: Int): Boolean { | ||
| val userId = userInfoRepository.getUserIdFromDataStore()?.toIntOrNull() ?: return false | ||
| val userIdString = userInfoRepository.getUserIdFromDataStore() | ||
| val userId = userIdString?.toIntOrNull() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Not sure if you need to separate into two variables if only used once |
||
|
|
||
| if (userId == null) { | ||
| Log.e("CheckInRepository", "Missing or invalid userId in DataStore: $userIdString") | ||
| return false | ||
| } | ||
|
|
||
| val time = Instant.now().toString() | ||
|
|
||
| return try { | ||
| val response = apolloClient | ||
| .mutation(LogWorkoutMutation(facilityId = gymId, workoutTime = time, id = userId )) | ||
| .mutation( | ||
| LogWorkoutMutation( | ||
| facilityId = gymId, | ||
| workoutTime = time, | ||
| userId = userId | ||
| ) | ||
| ) | ||
| .execute() | ||
|
|
||
| val ok = response.data?.logWorkout?.workoutFields != null && !response.hasErrors() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| package com.cornellappdev.uplift.data.repositories | ||
|
|
||
| import android.util.Log | ||
| import com.apollographql.apollo.ApolloClient | ||
| import com.cornellappdev.uplift.GetUserByNetIdQuery | ||
| import com.cornellappdev.uplift.GetWeeklyWorkoutDaysQuery | ||
| import com.cornellappdev.uplift.SetWorkoutGoalsMutation | ||
| import com.cornellappdev.uplift.data.models.ProfileData | ||
| import com.cornellappdev.uplift.data.models.WorkoutDomain | ||
| import java.time.Instant | ||
| import javax.inject.Inject | ||
| import javax.inject.Singleton | ||
|
|
||
|
|
||
| @Singleton | ||
| class ProfileRepository @Inject constructor( | ||
| private val userInfoRepository: UserInfoRepository, | ||
| private val apolloClient: ApolloClient | ||
| ) { | ||
| suspend fun getProfile(): Result<ProfileData> = runCatching { | ||
| val netId = userInfoRepository.getNetIdFromDataStore() | ||
| ?: throw IllegalStateException("NetId missing") | ||
|
|
||
| val userResponse = apolloClient.query( | ||
| GetUserByNetIdQuery(netId) | ||
| ).execute() | ||
|
|
||
| if (userResponse.hasErrors()) { | ||
| Log.e("ProfileRepo", "User query errors: ${userResponse.errors}") | ||
| throw IllegalStateException("User query failed") | ||
| } | ||
|
|
||
| val user = userResponse.data?.getUserByNetId?.firstOrNull()?.userFields | ||
| ?: throw IllegalStateException("User not found") | ||
|
|
||
| val userId = user.id.toIntOrNull() | ||
| ?: throw IllegalStateException("Invalid user ID: ${user.id}") | ||
|
|
||
| val weeklyResponse = apolloClient.query(GetWeeklyWorkoutDaysQuery(userId)).execute() | ||
|
|
||
| if (weeklyResponse.hasErrors()) { | ||
| throw IllegalStateException("Weekly workout days query failed: ${weeklyResponse.errors}") | ||
| } | ||
|
|
||
| val workouts = user.workoutHistory?.filterNotNull().orEmpty() | ||
|
|
||
| val workoutDomain = workouts.map { | ||
| WorkoutDomain( | ||
| gymName = it.workoutFields.gymName, | ||
| timestamp = Instant.parse(it.workoutFields.workoutTime.toString()) | ||
| .toEpochMilli() | ||
| ) | ||
| } | ||
|
Comment on lines
+47
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check how DateTime scalar is configured in Apollo setup
rg -n "DateTime|scalar" --type=kotlin app/src/main/java/com/cornellappdev/uplift/
fd -e kt -x grep -l "ScalarAdapter\|customScalarAdapters" {} \;Repository: cuappdev/uplift-android Length of output: 1148 🏁 Script executed: #!/bin/bash
# 1. Check the ProfileRepository.kt file context around lines 47-53
echo "=== ProfileRepository.kt context ==="
sed -n '40,65p' app/src/main/java/com/cornellappdev/uplift/data/repositories/ProfileRepository.kt
# 2. Find GraphQL schema files
echo -e "\n=== GraphQL schema files ==="
find . -name "*.graphql" -o -name "schema.json" | head -20
# 3. Check Apollo configuration
echo -e "\n=== Apollo configuration files ==="
find . -name "apollo.config.*" -o -name "build.gradle*" | head -20Repository: cuappdev/uplift-android Length of output: 1410 🏁 Script executed: #!/bin/bash
# 1. Check User.graphql for workoutTime and DateTime definition
echo "=== User.graphql content ==="
cat app/src/main/graphql/User.graphql
# 2. Check app/build.gradle for Apollo configuration
echo -e "\n=== app/build.gradle Apollo configuration ==="
grep -A 20 -B 5 "apollo\|scalar" app/build.gradle | head -100
# 3. Look for generated Apollo code
echo -e "\n=== Generated Apollo directories ==="
find app -type d -name "generated" -o -type d -name "apolloClient" -o -type d -name "apollo" | head -20Repository: cuappdev/uplift-android Length of output: 4116 🏁 Script executed: #!/bin/bash
# 1. Find generated Apollo types
echo "=== Looking for generated Apollo types ==="
find app/build -type f -name "*.kt" 2>/dev/null | grep -i "user\|workout" | head -20
# 2. Check if there's a build directory
echo -e "\n=== Build generated directories ==="
find app/build/generated -type d 2>/dev/null | head -20
# 3. Search for any kotlin file that might show DateTime definition
echo -e "\n=== Searching for DateTime type definition in generated code ==="
find app/build -type f -name "*.kt" 2>/dev/null -exec grep -l "DateTime\|workoutTime" {} \; | head -10
# 4. If generated files exist, check one
if [ -f "$(find app/build/generated -name '*User*.kt' 2>/dev/null | head -1)" ]; then
echo -e "\n=== Sample generated User type ==="
cat "$(find app/build/generated -name '*User*.kt' 2>/dev/null | head -1)" | head -100
fiRepository: cuappdev/uplift-android Length of output: 212 🏁 Script executed: #!/bin/bash
# 1. Search for usage of workoutTime elsewhere in the codebase
echo "=== Usage of workoutTime in codebase ==="
rg -n "workoutTime" --type=kotlin app/src/
# 2. Check if there are any DateTime-related imports in ProfileRepository
echo -e "\n=== ProfileRepository.kt full context (1-80 lines) ==="
head -80 app/src/main/java/com/cornellappdev/uplift/data/repositories/ProfileRepository.ktRepository: cuappdev/uplift-android Length of output: 3430 🌐 Web query:
💡 Result: In Apollo Kotlin 4.1.1 (Android), a GraphQL Option A (quick): map
|
||
|
|
||
| val weeklyDays = weeklyResponse.data?.getWeeklyWorkoutDays?.filterNotNull().orEmpty() | ||
|
|
||
| ProfileData( | ||
| name = user.name, | ||
| netId = user.netId, | ||
| encodedImage = user.encodedImage, | ||
| totalGymDays = user.totalGymDays, | ||
| activeStreak = user.activeStreak, | ||
| maxStreak = user.maxStreak, | ||
| streakStart = user.streakStart?.toString(), | ||
| workoutGoal = user.workoutGoal ?: 0, | ||
| workouts = workoutDomain, | ||
| weeklyWorkoutDays = weeklyDays | ||
| ) | ||
| }.onFailure { e -> | ||
| Log.e("ProfileRepo", "Failed to load profile", e) | ||
| } | ||
|
|
||
| suspend fun setWorkoutGoal(goal: Int): Result<Unit> = runCatching { | ||
| val userId = userInfoRepository.getUserIdFromDataStore()?.toIntOrNull() | ||
| ?: throw IllegalStateException("Missing user ID") | ||
|
|
||
| val response = apolloClient | ||
| .mutation( | ||
| SetWorkoutGoalsMutation( | ||
| userId = userId, | ||
| workoutGoal = goal | ||
| ) | ||
| ) | ||
| .execute() | ||
|
|
||
| if (response.hasErrors()) { | ||
| throw IllegalStateException("Goal update failed") | ||
| } | ||
| }.onFailure { e -> | ||
| Log.e("ProfileRepo", "Failed to update workout goal", e) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: cuappdev/uplift-android
Length of output: 113
Align field nullability with GraphQL schema.
activeStreakandmaxStreakare declared asInt!(non-nullable) in the GraphQL schema, but defined asInt?here. Update to match the schema contract.🤖 Prompt for AI Agents