diff --git a/app/src/main/java/com/modarb/android/network/ApiService.kt b/app/src/main/java/com/modarb/android/network/ApiService.kt index d2bf06e..99c0935 100644 --- a/app/src/main/java/com/modarb/android/network/ApiService.kt +++ b/app/src/main/java/com/modarb/android/network/ApiService.kt @@ -3,6 +3,7 @@ package com.modarb.android.network import com.modarb.android.network.models.BaseResponse import com.modarb.android.ui.home.ui.home.domain.models.HomePageResponse import com.modarb.android.ui.home.ui.plan.domain.models.PlanPageResponse +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.CustomWorkoutResponse import com.modarb.android.ui.onboarding.models.LoginResponse import com.modarb.android.ui.onboarding.models.RequestModels.LoginRequest import com.modarb.android.ui.onboarding.models.RequestModels.RegisterRequest @@ -51,7 +52,7 @@ interface ApiService { @GET("api/v1/user/templates/") suspend fun getCustomWorkouts( @Header("Authorization") token: String - ): Response + ): Response } diff --git a/app/src/main/java/com/modarb/android/network/NetworkHelper.kt b/app/src/main/java/com/modarb/android/network/NetworkHelper.kt index 3269805..1f0981e 100644 --- a/app/src/main/java/com/modarb/android/network/NetworkHelper.kt +++ b/app/src/main/java/com/modarb/android/network/NetworkHelper.kt @@ -5,6 +5,9 @@ import android.os.Build import android.widget.Toast import com.modarb.android.R import com.modarb.android.network.models.BaseResponse +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.Locale class NetworkHelper { @@ -40,5 +43,17 @@ class NetworkHelper { } + fun getDate(timestamp: String): String { + val dateTime = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + LocalDateTime.parse(timestamp, DateTimeFormatter.ISO_DATE_TIME) + } else { + TODO("VERSION.SDK_INT < O") + } + + val formattedDate = + dateTime.format(DateTimeFormatter.ofPattern("EEEE, d MMM", Locale.ENGLISH)) + return formattedDate + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/helpers/WorkoutData.kt b/app/src/main/java/com/modarb/android/ui/home/helpers/WorkoutData.kt index 75721ae..3e9f14f 100644 --- a/app/src/main/java/com/modarb/android/ui/home/helpers/WorkoutData.kt +++ b/app/src/main/java/com/modarb/android/ui/home/helpers/WorkoutData.kt @@ -57,5 +57,23 @@ object WorkoutData { return null } + fun getTotalExerciseTime(exercise: List): String { + var totalTime = 0 + for (ex in exercise) { + totalTime += ex.duration + } + return totalTime.toString() + } + + + // TODO handle getting reps after fixing the api +// fun getTotalExerciseReps(exercise: List): String { +// var totalReps = 0 +// for (ex in exercise) { +// totalReps += ex. +// } +// return totalReps.toString() +// } + } \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/CustomWorkoutTemplateAdapter.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/CustomWorkoutTemplateAdapter.kt index 799e5de..0c496cc 100644 --- a/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/CustomWorkoutTemplateAdapter.kt +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/CustomWorkoutTemplateAdapter.kt @@ -1,26 +1,35 @@ package com.modarb.android.ui.home.ui.plan.adapters +import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.modarb.android.R +import com.modarb.android.databinding.ItemCustomworkoutTemplateBinding +import com.modarb.android.network.NetworkHelper +import com.modarb.android.ui.home.helpers.WorkoutData +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.Data class CustomWorkoutTemplateAdapter( private val context: Context, - private val data: MutableList + private val data: List ) : RecyclerView.Adapter() { - inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) + inner class ViewHolder(val binding: ItemCustomworkoutTemplateBinding) : + RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(context) - .inflate(R.layout.item_customworkout_template, parent, false) - return ViewHolder(view) + val layoutInflater = LayoutInflater.from(context) + val binding = ItemCustomworkoutTemplateBinding.inflate(layoutInflater, parent, false) + return ViewHolder(binding) } + @SuppressLint("SetTextI18n") override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = data[position] + holder.binding.workoutName.text = item.name + holder.binding.workoutDate.text = NetworkHelper.getDate(item.creationDate) + holder.binding.timeTxt.text = WorkoutData.getTotalExerciseTime(item.exercises) + " min" } diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/MyPlanViewPagerAdapter.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/MyPlanViewPagerAdapter.kt index 8c6200a..ee8b9b2 100644 --- a/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/MyPlanViewPagerAdapter.kt +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/adapters/MyPlanViewPagerAdapter.kt @@ -13,11 +13,14 @@ import com.modarb.android.databinding.MyPlanViewBinding import com.modarb.android.ui.home.helpers.WorkoutData import com.modarb.android.ui.home.ui.plan.domain.models.Data import com.modarb.android.ui.home.ui.plan.domain.models.PlanPageResponse +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.CustomWorkoutResponse import com.modarb.android.ui.workout.activities.TodayWorkoutActivity import com.modarb.android.ui.workout.adapters.TrainingWeeksAdapter class MyPlanViewPagerAdapter( - private val context: Context, private var planResponse: PlanPageResponse + private val context: Context, + private var planResponse: PlanPageResponse, + private var customWorkoutResponse: CustomWorkoutResponse ) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -103,14 +106,10 @@ class MyPlanViewPagerAdapter( inner class CustomWorkoutViewHolder(private val binding: CustomWorkoutViewBinding) : RecyclerView.ViewHolder(binding.root) { - private val data = mutableListOf() fun bind(context: Context) { binding.recycleView.layoutManager = LinearLayoutManager(context) - for (i in 0..3) { - data.add("test") - } - val adapter = CustomWorkoutTemplateAdapter(context, data) + val adapter = CustomWorkoutTemplateAdapter(context, customWorkoutResponse.data) binding.recycleView.adapter = adapter binding.recycleView.isNestedScrollingEnabled = false } diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/data/PlanRepositoryImp.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/data/PlanRepositoryImp.kt index 532dfbd..cac0a95 100644 --- a/app/src/main/java/com/modarb/android/ui/home/ui/plan/data/PlanRepositoryImp.kt +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/data/PlanRepositoryImp.kt @@ -1,26 +1,15 @@ package com.modarb.android.ui.home.ui.plan.data -import android.content.Context import com.modarb.android.network.ApiResult import com.modarb.android.network.ApiService import com.modarb.android.ui.home.ui.plan.domain.MyPlanRepository import com.modarb.android.ui.home.ui.plan.domain.models.PlanPageResponse -import com.modarb.android.ui.onboarding.utils.UserPref.UserPrefUtil -import retrofit2.Response +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.CustomWorkoutResponse class PlanRepositoryImp(private val apiService: ApiService) : MyPlanRepository { - - // TODO handle get custom workouts - suspend fun getCustomWorkouts(context: Context): Response { - return apiService.getCustomWorkouts( - "Bearer " + UserPrefUtil.getUserData(context)!!.token - ) - } - override suspend fun getMyPlanPage( - workoutId: String, - token: String + workoutId: String, token: String ): ApiResult { return try { @@ -37,5 +26,21 @@ class PlanRepositoryImp(private val apiService: ApiService) : MyPlanRepository { } } + override suspend fun getCustomWorkouts(token: String): ApiResult { + return try { + val response = apiService.getCustomWorkouts(token) + if (response.isSuccessful) { + response.body()?.let { + ApiResult.Success(it) + } ?: ApiResult.Failure(Throwable("Response body is null")) + } else { + ApiResult.Error(response.body() as CustomWorkoutResponse) + + } + } catch (e: Exception) { + ApiResult.Failure(e) + } + } + } diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/MyPlanRepository.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/MyPlanRepository.kt index 231f4f1..efbb89d 100644 --- a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/MyPlanRepository.kt +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/MyPlanRepository.kt @@ -2,8 +2,10 @@ package com.modarb.android.ui.home.ui.plan.domain import com.modarb.android.network.ApiResult import com.modarb.android.ui.home.ui.plan.domain.models.PlanPageResponse +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.CustomWorkoutResponse interface MyPlanRepository { suspend fun getMyPlanPage(workoutId: String, token: String): ApiResult + suspend fun getCustomWorkouts(token: String): ApiResult } \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/CustomWorkoutResponse.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/CustomWorkoutResponse.kt new file mode 100644 index 0000000..c4ff259 --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/CustomWorkoutResponse.kt @@ -0,0 +1,8 @@ +package com.modarb.android.ui.home.ui.plan.domain.models.customworkout + +data class CustomWorkoutResponse( + val `data`: List, + val message: String, + val meta: Meta, + val status: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Data.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Data.kt new file mode 100644 index 0000000..60b578c --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Data.kt @@ -0,0 +1,9 @@ +package com.modarb.android.ui.home.ui.plan.domain.models.customworkout + +data class Data( + val creationDate: String, + val exercises: List, + val id: String, + val name: String, + val user: String +) \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Exercise.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Exercise.kt new file mode 100644 index 0000000..f5b8853 --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Exercise.kt @@ -0,0 +1,17 @@ +package com.modarb.android.ui.home.ui.plan.domain.models.customworkout + +data class Exercise( + val __v: Int, + val _id: String, + val benefits: String, + val category: String, + val coverImage: String, + val duration: Int, + val equipments: List, + val exerciseType: String, + val expectedDurationRange: ExpectedDurationRange, + val instructions: String, + val media: Media, + val name: String, + val targetMuscles: TargetMuscles +) \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/ExpectedDurationRange.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/ExpectedDurationRange.kt new file mode 100644 index 0000000..d4cac2b --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/ExpectedDurationRange.kt @@ -0,0 +1,6 @@ +package com.modarb.android.ui.home.ui.plan.domain.models.customworkout + +data class ExpectedDurationRange( + val max: Int, + val min: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Media.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Media.kt new file mode 100644 index 0000000..1e2786b --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Media.kt @@ -0,0 +1,6 @@ +package com.modarb.android.ui.home.ui.plan.domain.models.customworkout + +data class Media( + val type: String, + val url: String +) \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Meta.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Meta.kt new file mode 100644 index 0000000..da51f7c --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/Meta.kt @@ -0,0 +1,7 @@ +package com.modarb.android.ui.home.ui.plan.domain.models.customworkout + +data class Meta( + val page: Int, + val perPage: Int, + val total: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/TargetMuscles.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/TargetMuscles.kt new file mode 100644 index 0000000..ca98670 --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/models/customworkout/TargetMuscles.kt @@ -0,0 +1,6 @@ +package com.modarb.android.ui.home.ui.plan.domain.models.customworkout + +data class TargetMuscles( + val primary: String, + val secondary: String +) \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/usecase/GetCustomWorkoutUseCase.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/usecase/GetCustomWorkoutUseCase.kt new file mode 100644 index 0000000..0c873d5 --- /dev/null +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/domain/usecase/GetCustomWorkoutUseCase.kt @@ -0,0 +1,11 @@ +package com.modarb.android.ui.home.ui.plan.domain.usecase + +import com.modarb.android.network.ApiResult +import com.modarb.android.ui.home.ui.plan.domain.MyPlanRepository +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.CustomWorkoutResponse + +class GetCustomWorkoutUseCase(private var planRepository: MyPlanRepository) { + suspend fun invoke(token: String): ApiResult { + return planRepository.getCustomWorkouts(token) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/fragments/MyPlanFragment.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/fragments/MyPlanFragment.kt index 785be08..8b93c2f 100644 --- a/app/src/main/java/com/modarb/android/ui/home/ui/plan/fragments/MyPlanFragment.kt +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/fragments/MyPlanFragment.kt @@ -26,6 +26,7 @@ import com.modarb.android.ui.home.helpers.WorkoutData import com.modarb.android.ui.home.ui.plan.adapters.ExercisesAddAdapter import com.modarb.android.ui.home.ui.plan.adapters.MyPlanViewPagerAdapter import com.modarb.android.ui.home.ui.plan.domain.models.PlanPageResponse +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.CustomWorkoutResponse import com.modarb.android.ui.home.ui.plan.persentation.PlanViewModel import com.modarb.android.ui.onboarding.utils.UserPref.UserPrefUtil import com.modarb.android.ui.workout.domain.models.WorkoutModel @@ -56,6 +57,8 @@ class MyPlanFragment : Fragment() { initBottomSheet() handleAddCustomWorkout() initSelectBottomSheet() + getCustomWorkouts() + observeCombinedResponses() Log.e("workoutID", WorkoutData.workoutId) return root @@ -64,29 +67,53 @@ class MyPlanFragment : Fragment() { private fun observeData() { binding.progress.progressOverlay.visibility = View.VISIBLE +// lifecycleScope.launch { +// planViewModel.planResponse.collect { +// when (it) { +// is ApiResult.Error<*> -> handlePlanError(it as PlanPageResponse) +// is ApiResult.Failure -> handlePlanFail(it.exception) +// else -> {} +// } +// } +// } + + planViewModel.getPlanPage( + WorkoutData.workoutId, "Bearer " + UserPrefUtil.getUserData(requireContext())!!.token + ) + } + + private fun observeCombinedResponses() { lifecycleScope.launch { - planViewModel.planResponse.collect { - when (it) { - is ApiResult.Success<*> -> handlePlanSuccess(it.data as PlanPageResponse) - is ApiResult.Failure -> handlePlanError(it.exception) - else -> {} + planViewModel.combinedResponses.collect { combined -> + val planResponse = combined.first + val customWorkoutsResponse = combined.second + + if (planResponse is ApiResult.Success && customWorkoutsResponse is ApiResult.Success) { + handleBothSuccess( + planResponse.data, customWorkoutsResponse.data + ) } } } + } + + private fun handleBothSuccess( + planPageResponse: PlanPageResponse, customWorkoutResponse: CustomWorkoutResponse + ) { + Log.d("My plan page success", "Nice work") + initViewPager(planPageResponse, customWorkoutResponse) + WorkoutData.weekList = planPageResponse.data.weeks + binding.progress.progressOverlay.visibility = View.GONE - planViewModel.getPlanPage( - WorkoutData.workoutId, "Bearer " + UserPrefUtil.getUserData(requireContext())!!.token - ) } - private fun handlePlanError(exception: Throwable) { - // TODO handle error message + + private fun handlePlanFail(exception: Throwable) { Toast.makeText(requireContext(), exception.message, Toast.LENGTH_SHORT).show() binding.progress.progressOverlay.visibility = View.GONE } - private fun handlePlanSuccess(planPageResponse: PlanPageResponse) { - initViewPager(planPageResponse) + private fun handlePlanError(planPageResponse: PlanPageResponse) { WorkoutData.weekList = planPageResponse.data.weeks binding.progress.progressOverlay.visibility = View.GONE } @@ -98,6 +125,19 @@ class MyPlanFragment : Fragment() { } } + private fun getCustomWorkouts() { + lifecycleScope.launch { + planViewModel.customWorkoutResponse.collect { + when (it) { + is ApiResult.Error<*> -> handlePlanError(it as PlanPageResponse) + is ApiResult.Failure -> handlePlanFail(it.exception) + else -> {} + } + } + } + planViewModel.getCustomWorkouts("Bearer ${UserPrefUtil.getUserData(requireContext())?.token}") + } + private fun initBottomSheet() { bottomSheet = BottomSheetDialog(requireContext()) @@ -181,10 +221,12 @@ class MyPlanFragment : Fragment() { spinner.adapter = spinnerAdapter } - private fun initViewPager(planResponse: PlanPageResponse) { + private fun initViewPager( + planResponse: PlanPageResponse, customWorkoutResponse: CustomWorkoutResponse + ) { - val adapter = MyPlanViewPagerAdapter(requireContext(), planResponse) + val adapter = MyPlanViewPagerAdapter(requireContext(), planResponse, customWorkoutResponse) binding.viewPager.adapter = adapter binding.toggleButtonGroup.addOnButtonCheckedListener { _, checkedId, isChecked -> diff --git a/app/src/main/java/com/modarb/android/ui/home/ui/plan/persentation/PlanViewModel.kt b/app/src/main/java/com/modarb/android/ui/home/ui/plan/persentation/PlanViewModel.kt index 27d86ad..1c00d0e 100644 --- a/app/src/main/java/com/modarb/android/ui/home/ui/plan/persentation/PlanViewModel.kt +++ b/app/src/main/java/com/modarb/android/ui/home/ui/plan/persentation/PlanViewModel.kt @@ -6,9 +6,12 @@ import com.modarb.android.network.ApiResult import com.modarb.android.network.RetrofitService import com.modarb.android.ui.home.ui.plan.data.PlanRepositoryImp import com.modarb.android.ui.home.ui.plan.domain.models.PlanPageResponse +import com.modarb.android.ui.home.ui.plan.domain.models.customworkout.CustomWorkoutResponse +import com.modarb.android.ui.home.ui.plan.domain.usecase.GetCustomWorkoutUseCase import com.modarb.android.ui.home.ui.plan.domain.usecase.PlanPageUseCase import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch class PlanViewModel : ViewModel() { @@ -17,13 +20,29 @@ class PlanViewModel : ViewModel() { private var apiService = RetrofitService.createService() private var myPlanRepository = PlanRepositoryImp(apiService) private var myPlanPageUseCase = PlanPageUseCase(myPlanRepository) + private var getCustomWorkoutUseCase = GetCustomWorkoutUseCase(myPlanRepository) private val _planPageResponse = MutableStateFlow?>(null) val planResponse: StateFlow?> get() = _planPageResponse -// private val _customWorkoutsResponse = MutableLiveData>() -// val customWorkoutsResponse: LiveData> = _customWorkoutsResponse + private var _customWorkoutsResponse = MutableStateFlow?>(null) + val customWorkoutResponse: StateFlow?> get() = _customWorkoutsResponse + + + private val _combinedResponses = + MutableStateFlow?, ApiResult?>>(null to null) + val combinedResponses: StateFlow?, ApiResult?>> get() = _combinedResponses + + init { + viewModelScope.launch { + combine(_planPageResponse, _customWorkoutsResponse) { planResponse, workoutsResponse -> + planResponse to workoutsResponse + }.collect { combined -> + _combinedResponses.value = combined + } + } + } fun getPlanPage(workoutId: String, token: String) { viewModelScope.launch { @@ -32,10 +51,10 @@ class PlanViewModel : ViewModel() { } } -// fun getCustomWorkouts(ctx: Context) { -// viewModelScope.launch { -// val response = planRepo.getCustomWorkouts(ctx) -// _customWorkoutsResponse.value = response -// } -// } + fun getCustomWorkouts(token: String) { + viewModelScope.launch { + val response = getCustomWorkoutUseCase.invoke(token) + _customWorkoutsResponse.value = response + } + } } \ No newline at end of file