Skip to content
This repository has been archived by the owner on Mar 18, 2024. It is now read-only.

Commit

Permalink
Improve create exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
AnkitSuda committed Nov 29, 2022
1 parent cecc320 commit c0ca499
Show file tree
Hide file tree
Showing 15 changed files with 410 additions and 111 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ dependencies {
implementation project(":modules:stopwatch")
implementation project(":modules:ui-customize-barbells")
implementation project(":modules:ui-muscle-selector")
implementation project(":modules:ui-exercise-category-selector")

implementation Deps.Kotlin.coroutinesAndroid
kapt Deps.Android.Lifecycle.compiler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.ankitsuda.rebound.ui.customizebarbells.edit.BarbellEditBottomSheet
import com.ankitsuda.rebound.ui.customizeplates.CustomizePlatesScreen
import com.ankitsuda.rebound.ui.customizeplates.edit.PlateEditBottomSheet
import com.ankitsuda.rebound.ui.exercise_details.ExerciseDetailScreen
import com.ankitsuda.rebound.ui.exercisecategoryselector.ExerciseCategorySelectorBottomSheet
import com.ankitsuda.rebound.ui.exercises.ExercisesScreen
import com.ankitsuda.rebound.ui.history.HistoryScreen
import com.ankitsuda.rebound.ui.home.HomeScreen
Expand Down Expand Up @@ -131,6 +132,7 @@ internal fun AppNavigation(
addSupersetSelector(navController)
addBarbellSelector(navController)
addMuscleSelector(navController)
addExerciseCategorySelector(navController)
}
}

Expand Down Expand Up @@ -431,6 +433,12 @@ private fun NavGraphBuilder.addMuscleSelector(navController: NavController) {
}
}

private fun NavGraphBuilder.addExerciseCategorySelector(navController: NavController) {
bottomSheetScreen(LeafScreen.ExerciseCategorySelector()) {
ExerciseCategorySelectorBottomSheet(navController)
}
}

/**
* Adds an [NavController.OnDestinationChangedListener] to this [NavController] and updates the
* returned [State] which is updated as the destination changes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,5 @@
<string name="add_barbell">Add barbell</string>
<string name="select_barbell">Select barbell</string>
<string name="select_muscle">Select muscle</string>
<string name="select_category">Select category</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ import javax.inject.Inject

class MusclesRepository @Inject constructor(private val musclesDao: MusclesDao) {
fun getMuscles() = musclesDao.getMuscles()
fun getMuscle(tag: String) = musclesDao.getMuscle(tag)
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,13 @@ const val JUNCTION_ID_KEY = "junction_id"
const val ENTRY_ID_KEY = "entry_id"
const val BARBELL_ID_KEY = "barbell_id"
const val MUSCLE_ID_KEY = "muscle_id"
const val EXERCISE_CATEGORY_TAG_KEY = "exercise_category_tag"

const val RESULT_EXERCISES_SCREEN_EXERCISE_ID = "result_exercises_screen_exercise_id"
const val RESULT_SUPERSET_SELECTOR_SUPERSET_ID_KEY = "result_superset_selector_superset_id"
const val RESULT_BARBELL_SELECTOR_KEY = "result_barbell_selector"
const val RESULT_MUSCLE_SELECTOR_KEY = "result_muscle_selector"
const val RESULT_EXERCISE_CATEGORY_SELECTOR_KEY = "result_exercise_category_selector"

interface Screen {
val route: String
Expand Down Expand Up @@ -539,6 +541,22 @@ sealed class LeafScreen(
"muscle_selector?$MUSCLE_ID_KEY=$selectedMuscleId"
}
}

data class ExerciseCategorySelector(override val route: String = "exercise_category_selector?$EXERCISE_CATEGORY_TAG_KEY={$EXERCISE_CATEGORY_TAG_KEY}") :
LeafScreen(
route = route,
arguments = listOf(
navArgument(EXERCISE_CATEGORY_TAG_KEY) {
type = NavType.StringType
nullable = true
},
)
) {
companion object {
fun createRoute(selectedCategoryTag: String? = null) =
"exercise_category_selector?$EXERCISE_CATEGORY_TAG_KEY=$selectedCategoryTag"
}
}
}

@OptIn(ExperimentalAnimationApi::class)
Expand Down
1 change: 1 addition & 0 deletions modules/ui-create-exercise/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ dependencies {
implementation project(":modules:core-data")
implementation project(":modules:navigation")
implementation project(":modules:ui-muscle-selector")
implementation project(":modules:ui-exercise-category-selector")

implementation Deps.Dagger.hilt
kapt Deps.Dagger.hiltCompiler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,29 @@

package com.ankitsuda.rebound.ui.create_exercise

import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.Done
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import com.ankitsuda.base.utils.extensions.getStateFlow
import com.ankitsuda.navigation.*
import com.ankitsuda.rebound.ui.components.AppTextField
import com.ankitsuda.rebound.ui.components.BottomSheetSurface
import com.ankitsuda.rebound.ui.components.TopBar
import com.ankitsuda.rebound.ui.components.TopBarIconButton
import com.ankitsuda.rebound.ui.create_exercise.components.ValueSelectorCard
import com.ankitsuda.rebound.ui.exercisecategoryselector.models.ExerciseCategorySelectorResult
import com.ankitsuda.rebound.ui.muscleselector.models.MuscleSelectorResult
import com.google.accompanist.flowlayout.FlowRow
import kotlinx.coroutines.flow.collectLatest
import timber.log.Timber
import kotlin.random.Random

@Composable
fun CreateExerciseScreen(
Expand All @@ -65,7 +54,10 @@ fun CreateExerciseScreen(
val noteValue by viewModel.note.collectAsState("")

val selectedCategory by viewModel.selectedCategory.collectAsState()
val selectedMuscle by viewModel.selectedMuscle.collectAsState("abductors")
val selectedMuscleTag by viewModel.selectedMuscleTag.collectAsState()
val selectedMuscle by viewModel.selectedMuscle.collectAsState(null)

val selectedCategoryTag = selectedCategory.tag

val isCreateBtnEnabled = nameValue.trim().isNotEmpty()

Expand All @@ -75,6 +67,12 @@ fun CreateExerciseScreen(
?.getStateFlow<MuscleSelectorResult?>(RESULT_MUSCLE_SELECTOR_KEY, null)
?.collectAsState()

// Observes results when Exercise Category Selector changes value of arg
val exerciseCategorySelectorResult = navController.currentBackStackEntry
?.savedStateHandle
?.getStateFlow<ExerciseCategorySelectorResult?>(RESULT_EXERCISE_CATEGORY_SELECTOR_KEY, null)
?.collectAsState()

LaunchedEffect(key1 = muscleSelectorResult) {
muscleSelectorResult?.value?.muscleId?.let {
viewModel.setPrimaryMuscle(it)
Expand All @@ -86,6 +84,17 @@ fun CreateExerciseScreen(
)
}

LaunchedEffect(key1 = exerciseCategorySelectorResult) {
exerciseCategorySelectorResult?.value?.categoryTag?.let {
viewModel.setCategory(it)
}

navController.currentBackStackEntry?.savedStateHandle?.set(
RESULT_EXERCISE_CATEGORY_SELECTOR_KEY,
null
)
}

BottomSheetSurface {
Column {
TopBar(
Expand Down Expand Up @@ -117,12 +126,13 @@ fun CreateExerciseScreen(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
.padding(16.dp)
.padding(20.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Column(
Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
// .padding(bottom = 8.dp)
) {
Text(
text = stringResource(id = R.string.name),
Expand All @@ -141,7 +151,7 @@ fun CreateExerciseScreen(
Column(
Modifier
.fillMaxWidth()
.padding(top = 8.dp, bottom = 8.dp)
// .padding(top = 8.dp, bottom = 8.dp)
) {

Text(
Expand All @@ -157,88 +167,30 @@ fun CreateExerciseScreen(
viewModel.setNote(it)
})
}
Column(
Modifier
.fillMaxWidth()
.padding(top = 8.dp, bottom = 8.dp)

) {
Text(
text = stringResource(id = R.string.category),
style = MaterialTheme.typography.caption,
color = Color(117, 117, 117)
)
Spacer(modifier = Modifier.height(8.dp))

Button(onClick = {
navigator.navigate(LeafScreen.MuscleSelector.createRoute(selectedMuscleId = selectedMuscle))
}) {
Text(text = "Select category")
ValueSelectorCard(
name = stringResource(id = R.string.category),
value = selectedCategoryTag,
onClick = {
navigator.navigate(
LeafScreen.ExerciseCategorySelector.createRoute(
selectedCategoryTag = selectedCategoryTag
)
)
}
// FlowRow(crossAxisSpacing = 8.dp) {
// for (category in categoriesList) {
// Row(
// modifier = Modifier
// .width((LocalConfiguration.current.screenWidthDp / 2.5).dp)
// .clickable(onClick = {
// viewModel.setCategory(category)
// }, indication = null,
// interactionSource = remember { MutableInteractionSource() }),
// verticalAlignment = Alignment.CenterVertically,
// ) {
// RadioButton(selected = selectedCategory == category, onClick = {
// viewModel.setCategory(category)
// })
// Spacer(modifier = Modifier.width(8.dp))
//
// Text(
// text = category.tag
// )
// }
// }
// }
}
Column(
Modifier
.fillMaxWidth()
.padding(top = 8.dp, bottom = 8.dp)

) {
Text(
text = stringResource(id = R.string.primary_muscle),
style = MaterialTheme.typography.caption,
color = Color(117, 117, 117)
)
Spacer(modifier = Modifier.height(8.dp))

Button(onClick = {
navigator.navigate(LeafScreen.MuscleSelector.createRoute(selectedMuscleId = selectedMuscle))
}) {
Text(text = "Select muscle")
)

ValueSelectorCard(
name = stringResource(id = R.string.primary_muscle),
value = selectedMuscle?.name ?: selectedMuscleTag,
onClick = {
navigator.navigate(
LeafScreen.MuscleSelector.createRoute(
selectedMuscleId = selectedMuscleTag
)
)
}
// FlowRow(crossAxisSpacing = 8.dp) {
// for (muscle in musclesList) {
// Row(
// modifier = Modifier
// .width((LocalConfiguration.current.screenWidthDp / 2.5).dp)
// .clickable(onClick =
// {
// viewModel.setPrimaryMuscle(muscle.tag)
// }, indication = null,
// interactionSource = remember { MutableInteractionSource() }),
// verticalAlignment = Alignment.CenterVertically,
// ) {
// RadioButton(selected = selectedMuscle == muscle.tag, onClick = {
// viewModel.setPrimaryMuscle(muscle.tag)
// })
// Spacer(modifier = Modifier.width(8.dp))
// Text(
// text = muscle.name,
// )
// }
// }
// }
}
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,21 @@

package com.ankitsuda.rebound.ui.create_exercise

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ankitsuda.base.utils.extensions.getStateFlow
import com.ankitsuda.navigation.RESULT_MUSCLE_SELECTOR_KEY
import com.ankitsuda.base.utils.extensions.shareWhileObserved
import com.ankitsuda.rebound.data.repositories.ExercisesRepository
import com.ankitsuda.rebound.data.repositories.MusclesRepository
import com.ankitsuda.rebound.domain.ExerciseCategory
import com.ankitsuda.rebound.domain.allExerciseCategories
import com.ankitsuda.rebound.ui.muscleselector.models.MuscleSelectorResult
import com.ankitsuda.rebound.domain.parseToExerciseCategory1
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class CreateExerciseScreenViewModel @Inject constructor(
private val handle: SavedStateHandle,
private val musclesRepository: MusclesRepository,
private val exercisesRepository: ExercisesRepository
) :
Expand All @@ -52,11 +46,18 @@ class CreateExerciseScreenViewModel @Inject constructor(
MutableStateFlow<ExerciseCategory>(ExerciseCategory.WeightAndReps)
val selectedCategory = _selectedCategory.asStateFlow()

private var _selectedMuscle = MutableStateFlow<String?>("abductors")
val selectedMuscle = _selectedMuscle.asStateFlow()
private var _selectedMuscleTag = MutableStateFlow<String?>(null)
val selectedMuscleTag = _selectedMuscleTag.asStateFlow()

val selectedMuscle = _selectedMuscleTag.flatMapLatest {
if (it != null) {
musclesRepository.getMuscle(it)
} else {
emptyFlow()
}
}.distinctUntilChanged()
.shareWhileObserved(viewModelScope)

// Dummy
// val allCategories = ExerciseCategory.values()
val allCategories = allExerciseCategories
val allPrimaryMuscles = musclesRepository.getMuscles()

Expand All @@ -72,16 +73,20 @@ class CreateExerciseScreenViewModel @Inject constructor(
_selectedCategory.value = value
}

fun setCategory(tag: String) {
setCategory(tag.parseToExerciseCategory1())
}

fun setPrimaryMuscle(value: String) {
_selectedMuscle.value = value
_selectedMuscleTag.value = value
}

fun createExercise() {
viewModelScope.launch {
exercisesRepository.createExercise(
name = _name.value,
notes = _note.value,
primaryMuscleTag = _selectedMuscle.value,
primaryMuscleTag = _selectedMuscleTag.value,
category = _selectedCategory.value
)
}
Expand Down
Loading

0 comments on commit c0ca499

Please sign in to comment.