From cf91c9d2b9ecaf09885eff23666faa3c07d0e3aa Mon Sep 17 00:00:00 2001 From: Ankit Suda Date: Mon, 28 Nov 2022 16:41:01 +0530 Subject: [PATCH] Support barbells in plate calculator and other fixes --- .../rebound/ui/navigation/AppNavigation.kt | 8 ++ .../workouteditor/ExercisePopupMenu.kt | 8 ++ .../workouteditor/WorkoutEditorComponent.kt | 34 ++++++- .../workouteditor/WorkoutExerciseItem.kt | 50 +++++----- .../BarbellSelectorBottomSheet.kt | 96 +++++++++++++++++++ .../BarbellSelectorBottomSheetViewModel.kt | 40 ++++++++ .../components/BarbellSelectorListItem.kt | 89 +++++++++++++++++ .../models/BarbellSelectorResult.kt | 24 +++++ .../WarmUpCalculatorDialog.kt | 2 +- .../warmupcalculator/WarmUpSetComponent.kt | 2 +- .../src/main/res/values/strings.xml | 1 + .../1.json | 12 ++- .../rebound/data/db/daos/WorkoutsDao.kt | 3 + .../data/repositories/WorkoutsRepository.kt | 5 + .../entities/ExerciseWorkoutJunction.kt | 3 + .../java/com/ankitsuda/navigation/Screens.kt | 21 ++++ .../ui/keyboard/NumKeysContainerComponent.kt | 2 +- .../keyboard/ReboundSetKeyboardComponent.kt | 15 ++- .../keyboard/ReboundSetKeyboardDemoScreen.kt | 5 +- .../ui/keyboard/ReboundSetKeyboardHost.kt | 28 +++--- .../ui/keyboard/RightLayoutComponent.kt | 2 +- .../ui/keyboard/enums/ReboundKeyboardType.kt | 19 ++-- .../ui/keyboard/field/ReboundSetTextField.kt | 4 +- .../PlateCalculatorComponent.kt | 19 +++- .../PlateCalculatorComponentViewModel.kt | 25 ++++- .../ui/workoutedit/WorkoutEditScreen.kt | 2 + .../rebound/ui/workout_panel/WorkoutPanel.kt | 7 +- .../ui/workout_panel/WorkoutPanelViewModel.kt | 22 ++++- 28 files changed, 473 insertions(+), 75 deletions(-) create mode 100644 modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheet.kt create mode 100644 modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheetViewModel.kt create mode 100644 modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/components/BarbellSelectorListItem.kt create mode 100644 modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/models/BarbellSelectorResult.kt diff --git a/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt b/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt index b6e3ec2a..4579ad0c 100644 --- a/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt @@ -25,6 +25,7 @@ import androidx.navigation.NavHostController import com.ankitsuda.common.compose.collectEvent import com.ankitsuda.navigation.* import com.ankitsuda.rebound.ui.calendar.CalendarScreen +import com.ankitsuda.rebound.ui.components.workouteditor.barbellselector.BarbellSelectorBottomSheet import com.ankitsuda.rebound.ui.components.workouteditor.supersetselector.SupersetSelectorBottomSheet import com.ankitsuda.rebound.ui.create_exercise.CreateExerciseScreen import com.ankitsuda.rebound.ui.customizebarbells.CustomizeBarbellsScreen @@ -127,6 +128,7 @@ internal fun AppNavigation( addExercisesBottomSheet(navController) addRestTimer(navController) addSupersetSelector(navController) + addBarbellSelector(navController) } } @@ -415,6 +417,12 @@ private fun NavGraphBuilder.addSupersetSelector(navController: NavController) { } } +private fun NavGraphBuilder.addBarbellSelector(navController: NavController) { + bottomSheetScreen(LeafScreen.BarbellSelector()) { + BarbellSelectorBottomSheet(navController) + } +} + /** * Adds an [NavController.OnDestinationChangedListener] to this [NavController] and updates the * returned [State] which is updated as the destination changes. diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/ExercisePopupMenu.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/ExercisePopupMenu.kt index f274e49c..f7ad394b 100644 --- a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/ExercisePopupMenu.kt +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/ExercisePopupMenu.kt @@ -20,6 +20,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import com.ankitsuda.rebound.domain.entities.Barbell @Composable @@ -32,6 +33,7 @@ fun ExercisePopupMenu( onAddWarmUpSets: () -> Unit, onAddNote: () -> Unit, onAddToSuperset: () -> Unit, + onChangeBarbell: () -> Unit, onRemoveFromSuperset: () -> Unit, ) { DropdownMenu( @@ -61,6 +63,12 @@ fun ExercisePopupMenu( }) { Text(stringResource(id = if (isInSuperset) R.string.remove_from_superset else R.string.add_to_superset)) } + DropdownMenuItem(onClick = { + onDismissRequest() + onChangeBarbell() + }) { + Text(stringResource(id = R.string.barbell)) + } DropdownMenuItem(onClick = { onDismissRequest() onDeleteExercise() diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutEditorComponent.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutEditorComponent.kt index 2be9324d..72b9e6b7 100644 --- a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutEditorComponent.kt +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutEditorComponent.kt @@ -36,11 +36,9 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import com.ankitsuda.common.compose.SAFE_RS_KEYBOARD_HEIGHT import com.ankitsuda.navigation.* -import com.ankitsuda.rebound.domain.entities.ExerciseLogEntry -import com.ankitsuda.rebound.domain.entities.ExerciseSetGroupNote -import com.ankitsuda.rebound.domain.entities.ExerciseWorkoutJunction -import com.ankitsuda.rebound.domain.entities.LogEntriesWithExerciseJunction +import com.ankitsuda.rebound.domain.entities.* import com.ankitsuda.rebound.ui.components.AppTextField +import com.ankitsuda.rebound.ui.components.workouteditor.barbellselector.models.BarbellSelectorResult import com.ankitsuda.rebound.ui.components.workouteditor.supersetselector.models.SupersetSelectorResult import com.ankitsuda.rebound.ui.components.workouteditor.warmupcalculator.toExerciseLogEntries import com.ankitsuda.rebound.ui.keyboard.LocalReboundSetKeyboard @@ -58,6 +56,7 @@ fun WorkoutEditorComponent( workoutNote: String?, cancelWorkoutButtonVisible: Boolean, logEntriesWithJunction: List, + barbells: List, onChangeWorkoutName: (String) -> Unit, onChangeWorkoutNote: (String) -> Unit, onAddExerciseToWorkout: (exerciseId: String) -> Unit, @@ -72,6 +71,7 @@ fun WorkoutEditorComponent( onChangeNote: (ExerciseSetGroupNote) -> Unit, onAddToSuperset: (junctionId: String, supersetId: Int) -> Unit, onRemoveFromSuperset: (LogEntriesWithExerciseJunction) -> Unit, + onUpdateBarbell: (junctionId: String, barbellId: String) -> Unit, layoutAtTop: @Composable LazyItemScope.() -> Unit = {} ) { // Observes results when ExercisesScreen changes value of arg @@ -85,6 +85,12 @@ fun WorkoutEditorComponent( ?.getStateFlow(RESULT_SUPERSET_SELECTOR_SUPERSET_ID_KEY, null) ?.collectAsState() + // Observes results when Barbell Selector changes value of arg + val barbellSelectorResult = navController.currentBackStackEntry + ?.savedStateHandle + ?.getStateFlow(RESULT_BARBELL_SELECTOR_KEY, null) + ?.collectAsState() + val navigationBarHeight = with(LocalDensity.current) { if (addNavigationBarPadding) LocalWindowInsets.current.navigationBars.bottom.toDp() else 0.dp } @@ -115,6 +121,17 @@ fun WorkoutEditorComponent( } } + LaunchedEffect(key1 = barbellSelectorResult?.value) { + barbellSelectorResult?.value?.let { result -> + onUpdateBarbell(result.junctionId, result.barbellId) + + navController.currentBackStackEntry?.savedStateHandle?.set( + RESULT_BARBELL_SELECTOR_KEY, + null + ) + } + } + val keyboardController = LocalSoftwareKeyboardController.current val reboundKeyboard = LocalReboundSetKeyboard.current fun hideKeyboard() { @@ -182,6 +199,7 @@ fun WorkoutEditorComponent( workoutExerciseItemAlt( useReboundKeyboard = useReboundKeyboard, logEntriesWithJunction = logEntriesWithJunctionItem, + barbells = barbells, onValuesUpdated = { updatedEntry -> onUpdateLogEntry(updatedEntry) }, @@ -223,6 +241,14 @@ fun WorkoutEditorComponent( junctionId = logEntriesWithJunctionItem.junction.id, ) ) + }, + onRequestBarbellChanger = { + navigator.navigate( + LeafScreen.BarbellSelector.createRoute( + junctionId = logEntriesWithJunctionItem.junction.id, + selectedBarbellId = logEntriesWithJunctionItem.junction.barbellId + ) + ) } ) } diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutExerciseItem.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutExerciseItem.kt index 42419952..b1a0c8bb 100644 --- a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutExerciseItem.kt +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/WorkoutExerciseItem.kt @@ -43,10 +43,7 @@ import com.ankitsuda.rebound.domain.DistanceUnit import com.ankitsuda.rebound.domain.ExerciseCategory import com.ankitsuda.rebound.domain.LogSetType import com.ankitsuda.rebound.domain.WeightUnit -import com.ankitsuda.rebound.domain.entities.Exercise -import com.ankitsuda.rebound.domain.entities.ExerciseLogEntry -import com.ankitsuda.rebound.domain.entities.ExerciseSetGroupNote -import com.ankitsuda.rebound.domain.entities.LogEntriesWithExerciseJunction +import com.ankitsuda.rebound.domain.entities.* import com.ankitsuda.rebound.ui.components.RButton import com.ankitsuda.rebound.ui.components.RSpacer import com.ankitsuda.rebound.ui.components.workouteditor.warmupcalculator.WarmUpCalculatorDialog @@ -64,6 +61,7 @@ private val ExerciseLogEntryComparator = Comparator { left, ri fun LazyListScope.workoutExerciseItemAlt( useReboundKeyboard: Boolean = false, logEntriesWithJunction: LogEntriesWithExerciseJunction, + barbells: List, onUpdateWarmUpSets: (List) -> Unit, onValuesUpdated: (updatedEntry: ExerciseLogEntry) -> Unit, onSwipeDelete: (ExerciseLogEntry) -> Unit, @@ -74,6 +72,7 @@ fun LazyListScope.workoutExerciseItemAlt( onChangeNote: (ExerciseSetGroupNote) -> Unit, onAddToSuperset: () -> Unit, onRemoveFromSuperset: () -> Unit, + onRequestBarbellChanger: () -> Unit, ) { val supersetId = logEntriesWithJunction.junction.supersetId @@ -81,6 +80,8 @@ fun LazyListScope.workoutExerciseItemAlt( val logEntries = logEntriesWithJunction.logEntries val notes = logEntriesWithJunction.notes ?: emptyList() val sortedEntries = logEntries.sortedWith(ExerciseLogEntryComparator) + val barbell = barbells.find { b -> b.id == logEntriesWithJunction.junction.barbellId } + // Exercise info item(key = "${logEntriesWithJunction.junction.id}_exercise_info") { @@ -177,7 +178,8 @@ fun LazyListScope.workoutExerciseItemAlt( }, onAddNote = onAddEmptyNote, onAddToSuperset = onAddToSuperset, - onRemoveFromSuperset = onRemoveFromSuperset + onRemoveFromSuperset = onRemoveFromSuperset, + onChangeBarbell = onRequestBarbellChanger, ) } @@ -302,12 +304,14 @@ fun LazyListScope.workoutExerciseItemAlt( it.entryId }) { entry -> val appSettings = LocalAppSettings.current - key(appSettings.weightUnit, appSettings.distanceUnit) { + + key(barbell, appSettings.weightUnit, appSettings.distanceUnit) { SetItem( useReboundKeyboard = useReboundKeyboard, revisedSetText = revisedSetsTexts[sortedEntries.indexOf(entry)], exercise = exercise, exerciseLogEntry = entry, + barbell = barbell, onChange = { onValuesUpdated(it) }, @@ -349,6 +353,7 @@ fun LazyListScope.workoutExerciseItemAlt( private fun LazyItemScope.SetItem( exercise: Exercise, useReboundKeyboard: Boolean, + barbell: Barbell?, revisedSetText: Pair, exerciseLogEntry: ExerciseLogEntry, onChange: (ExerciseLogEntry) -> Unit, @@ -358,14 +363,6 @@ private fun LazyItemScope.SetItem( mutableStateOf(exerciseLogEntry) } -// LaunchedEffect(key1 = exerciseLogEntry) { -// // We have to change saved rpe manually because -// // main rpe change is not handled by SetItem function -// if (exerciseLogEntry.rpe != mLogEntry.rpe) { -// mLogEntry = mLogEntry.copy(rpe = exerciseLogEntry.rpe) -// } -// } - val completionAnimDuration = 200 val completionAnimSpecFloat = tween( @@ -461,6 +458,7 @@ private fun LazyItemScope.SetItem( exercise = exercise, exerciseLogEntry = mLogEntry, revisedSetText = revisedSetText, + barbell = barbell, onWeightChange = { _, value -> handleOnChange(mLogEntry.copy(weight = value)) }, @@ -494,6 +492,7 @@ private fun SetItemLayout( exercise: Exercise, exerciseLogEntry: ExerciseLogEntry, revisedSetText: Pair, + barbell: Barbell?, onWeightChange: (ExerciseLogEntry, Double?) -> Unit, onRepsChange: (ExerciseLogEntry, Int?) -> Unit, onDistanceChange: (ExerciseLogEntry, Double?) -> Unit, @@ -598,7 +597,8 @@ private fun SetItemLayout( bgColor = bgColor, reboundKeyboardType = getReboundKeyboardType( category = exercise.category, - isLeft = true + isLeft = true, + barbell = barbell, ), ) } else { @@ -663,7 +663,7 @@ private fun SetItemLayout( }, contentColor = contentColor, bgColor = bgColor, - reboundKeyboardType = ReboundKeyboardType.RPE + reboundKeyboardType = ReboundKeyboardType.Rpe ) } @@ -696,11 +696,17 @@ private fun SetItemLayout( } } -private fun getReboundKeyboardType(category: ExerciseCategory, isLeft: Boolean) = +private fun getReboundKeyboardType( + category: ExerciseCategory, + isLeft: Boolean, + barbell: Barbell? = null +) = when (category) { - ExerciseCategory.WEIGHTS_AND_REPS -> if (isLeft) ReboundKeyboardType.WEIGHT else ReboundKeyboardType.REPS - ExerciseCategory.REPS -> ReboundKeyboardType.REPS - ExerciseCategory.DISTANCE_AND_TIME -> if (isLeft) ReboundKeyboardType.DISTANCE else ReboundKeyboardType.TIME - ExerciseCategory.TIME -> ReboundKeyboardType.TIME - ExerciseCategory.UNKNOWN -> ReboundKeyboardType.REPS + ExerciseCategory.WEIGHTS_AND_REPS -> if (isLeft) ReboundKeyboardType.Weight( + barbell = barbell + ) else ReboundKeyboardType.Reps + ExerciseCategory.REPS -> ReboundKeyboardType.Reps + ExerciseCategory.DISTANCE_AND_TIME -> if (isLeft) ReboundKeyboardType.Distance else ReboundKeyboardType.Time + ExerciseCategory.TIME -> ReboundKeyboardType.Time + ExerciseCategory.UNKNOWN -> ReboundKeyboardType.Reps } diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheet.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheet.kt new file mode 100644 index 00000000..69c7880e --- /dev/null +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheet.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 Ankit Suda. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package com.ankitsuda.rebound.ui.components.workouteditor.barbellselector + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController +import com.ankitsuda.navigation.LocalNavigator +import com.ankitsuda.navigation.Navigator +import com.ankitsuda.navigation.RESULT_BARBELL_SELECTOR_KEY +import com.ankitsuda.navigation.RESULT_SUPERSET_SELECTOR_SUPERSET_ID_KEY +import com.ankitsuda.rebound.domain.entities.Barbell +import com.ankitsuda.rebound.domain.entities.ExerciseWorkoutJunction +import com.ankitsuda.rebound.ui.components.BottomSheetSurface +import com.ankitsuda.rebound.ui.components.TopBar +import com.ankitsuda.rebound.ui.components.workouteditor.R +import com.ankitsuda.rebound.ui.components.workouteditor.barbellselector.components.BarbellSelectorListItem +import com.ankitsuda.rebound.ui.components.workouteditor.barbellselector.models.BarbellSelectorResult +import com.ankitsuda.rebound.ui.components.workouteditor.supersetselector.components.SupersetExerciseListItem +import com.ankitsuda.rebound.ui.components.workouteditor.supersetselector.models.SupersetSelectorResult + +@Composable +fun BarbellSelectorBottomSheet( + navController: NavController, + navigator: Navigator = LocalNavigator.current, + viewModel: BarbellSelectorBottomSheetViewModel = hiltViewModel() +) { + val junctionId = viewModel.junctionId + val initialSelectedBarbellId = viewModel.selectedBarbellId + val barbells by viewModel.barbells.collectAsState(initial = emptyList()) + + fun handleItemClick(barbell: Barbell) { + navController.previousBackStackEntry?.savedStateHandle?.set( + RESULT_BARBELL_SELECTOR_KEY, + BarbellSelectorResult( + junctionId = junctionId, + barbellId = barbell.id, + ) + ) + navController.popBackStack() + } + + BottomSheetSurface { + LazyColumn( + Modifier + .fillMaxWidth(), + contentPadding = + WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom) + .asPaddingValues() + ) { + item { + TopBar( + title = stringResource(id = R.string.select_barbell), + statusBarEnabled = false, + elevationEnabled = false + ) + } + + itemsIndexed(barbells, key = { _, barbell -> + barbell.id + }) { index, barbell -> + BarbellSelectorListItem( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = if (index == barbells.size - 1) 8.dp else 0.dp), + barbell = barbell, + isSelected = barbell.id == initialSelectedBarbellId, + onClick = { + handleItemClick(barbell) + } + ) + } + } + } +} \ No newline at end of file diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheetViewModel.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheetViewModel.kt new file mode 100644 index 00000000..f00cfc78 --- /dev/null +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/BarbellSelectorBottomSheetViewModel.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Ankit Suda. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package com.ankitsuda.rebound.ui.components.workouteditor.barbellselector + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.ankitsuda.base.utils.extensions.shareWhileObserved +import com.ankitsuda.navigation.BARBELL_ID_KEY +import com.ankitsuda.navigation.JUNCTION_ID_KEY +import com.ankitsuda.navigation.WORKOUT_ID_KEY +import com.ankitsuda.rebound.data.repositories.BarbellsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.distinctUntilChanged +import javax.inject.Inject + +@HiltViewModel +class BarbellSelectorBottomSheetViewModel @Inject constructor( + handle: SavedStateHandle, + barbellsRepository: BarbellsRepository, +) : ViewModel() { + val junctionId = requireNotNull(handle.get(JUNCTION_ID_KEY)) + val selectedBarbellId = handle.get(BARBELL_ID_KEY) + + val barbells = barbellsRepository.getActiveBarbells() + .distinctUntilChanged() + .shareWhileObserved(viewModelScope) +} \ No newline at end of file diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/components/BarbellSelectorListItem.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/components/BarbellSelectorListItem.kt new file mode 100644 index 00000000..8e1a56d8 --- /dev/null +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/components/BarbellSelectorListItem.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Ankit Suda. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package com.ankitsuda.rebound.ui.components.workouteditor.barbellselector.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Switch +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Check +import androidx.compose.material.icons.outlined.MoreVert +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.ankitsuda.base.util.colorFromSupersetId +import com.ankitsuda.base.util.kgToReadable +import com.ankitsuda.base.util.lbsToReadable +import com.ankitsuda.common.compose.R +import com.ankitsuda.common.compose.localizedStr +import com.ankitsuda.rebound.domain.WeightUnit +import com.ankitsuda.rebound.domain.entities.Barbell +import com.ankitsuda.rebound.ui.components.RSpacer +import com.ankitsuda.rebound.ui.theme.ReboundTheme + +@Composable +internal fun BarbellSelectorListItem( + modifier: Modifier = Modifier, + barbell: Barbell, + isSelected: Boolean, + onClick: () -> Unit +) { + Box( + modifier = modifier + ) { + Row( + modifier = Modifier + .clickable(onClick = onClick) + .padding(start = 24.dp, end = 24.dp, bottom = 12.dp, top = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = barbell.name ?: "", + style = ReboundTheme.typography.body1, + color = ReboundTheme.colors.onBackground + ) + Text( + text = "${ + barbell.weightKg?.kgToReadable() + } ${WeightUnit.KG.localizedStr()} / ${ + barbell.weightLbs?.lbsToReadable() + } ${WeightUnit.LBS.localizedStr()}", + style = ReboundTheme.typography.body2, + color = ReboundTheme.colors.onBackground.copy(alpha = 0.75f) + ) + } + + if (isSelected) { + Icon( + imageVector = Icons.Outlined.Check, + contentDescription = stringResource(R.string.selected), + tint = ReboundTheme.colors.primary + ) + } + } + + } +} \ No newline at end of file diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/models/BarbellSelectorResult.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/models/BarbellSelectorResult.kt new file mode 100644 index 00000000..f07f2328 --- /dev/null +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/barbellselector/models/BarbellSelectorResult.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Ankit Suda. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package com.ankitsuda.rebound.ui.components.workouteditor.barbellselector.models + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class BarbellSelectorResult( + val junctionId: String, + val barbellId: String +) : Parcelable diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpCalculatorDialog.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpCalculatorDialog.kt index f863ae80..fd914a4f 100644 --- a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpCalculatorDialog.kt +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpCalculatorDialog.kt @@ -172,7 +172,7 @@ private fun WarmUpCalculatorDialogLayout( } ?: 0.0 ) }, - reboundKeyboardType = ReboundKeyboardType.WEIGHT, + reboundKeyboardType = ReboundKeyboardType.Weight(), ) } diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpSetComponent.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpSetComponent.kt index c05a60c6..a3afc8c0 100644 --- a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpSetComponent.kt +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/warmupcalculator/WarmUpSetComponent.kt @@ -96,7 +96,7 @@ internal fun WarmUpSetComponent( value = startingSet.findFormula(), contentColor = fieldContentColor, bgColor = bgColor, - reboundKeyboardType = ReboundKeyboardType.WARMUP_SET, + reboundKeyboardType = ReboundKeyboardType.WarmupSet, onValueChange = { val arr = it.split(" x ") val weightInt = diff --git a/modules/common-ui-resources/src/main/res/values/strings.xml b/modules/common-ui-resources/src/main/res/values/strings.xml index a38f2da0..8adf195f 100644 --- a/modules/common-ui-resources/src/main/res/values/strings.xml +++ b/modules/common-ui-resources/src/main/res/values/strings.xml @@ -258,4 +258,5 @@ Barbells Barbell Add barbell + Select barbell \ No newline at end of file diff --git a/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json b/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json index f2a35d12..dd7a6c37 100644 --- a/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json +++ b/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "68617553ff75764b3f0264b855b8cd96", + "identityHash": "0df17bb8a8b397dd92836c9e2bad51c5", "entities": [ { "tableName": "body_parts", @@ -408,7 +408,7 @@ }, { "tableName": "exercise_workout_junctions", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `superset_id` INTEGER, `exercise_id` TEXT, `workout_id` TEXT, PRIMARY KEY(`id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `superset_id` INTEGER, `barbell_id` TEXT, `exercise_id` TEXT, `workout_id` TEXT, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", @@ -422,6 +422,12 @@ "affinity": "INTEGER", "notNull": false }, + { + "fieldPath": "barbellId", + "columnName": "barbell_id", + "affinity": "TEXT", + "notNull": false + }, { "fieldPath": "exerciseId", "columnName": "exercise_id", @@ -902,7 +908,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '68617553ff75764b3f0264b855b8cd96')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0df17bb8a8b397dd92836c9e2bad51c5')" ] } } \ No newline at end of file diff --git a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/daos/WorkoutsDao.kt b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/daos/WorkoutsDao.kt index 343d3b4b..a726681d 100644 --- a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/daos/WorkoutsDao.kt +++ b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/daos/WorkoutsDao.kt @@ -209,6 +209,9 @@ AND is_hidden = 0 AND in_progress = 0 GROUP BY start_at) @Query("UPDATE exercise_workout_junctions SET superset_id = :supersetId WHERE id = :junctionId") suspend fun updateExerciseWorkoutJunctionSupersetId(junctionId: String, supersetId: Int?) + @Query("UPDATE exercise_workout_junctions SET barbell_id = :barbellId WHERE id = :junctionId") + suspend fun updateExerciseWorkoutJunctionBarbellId(junctionId: String, barbellId: String?) + @Query( """ SELECT * FROM workouts w diff --git a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/WorkoutsRepository.kt b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/WorkoutsRepository.kt index 3009bed4..a99d773d 100644 --- a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/WorkoutsRepository.kt +++ b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/WorkoutsRepository.kt @@ -517,4 +517,9 @@ class WorkoutsRepository @Inject constructor( suspend fun updateExerciseWorkoutJunctionSupersetId(junctionId: String, supersetId: Int?) { workoutsDao.updateExerciseWorkoutJunctionSupersetId(junctionId, supersetId) } + + suspend fun updateExerciseWorkoutJunctionBarbellId(junctionId: String, barbellId: String) { + workoutsDao.updateExerciseWorkoutJunctionBarbellId(junctionId, barbellId) + } + } \ No newline at end of file diff --git a/modules/domain/src/main/java/com/ankitsuda/rebound/domain/entities/ExerciseWorkoutJunction.kt b/modules/domain/src/main/java/com/ankitsuda/rebound/domain/entities/ExerciseWorkoutJunction.kt index 01e6e172..a033d90a 100644 --- a/modules/domain/src/main/java/com/ankitsuda/rebound/domain/entities/ExerciseWorkoutJunction.kt +++ b/modules/domain/src/main/java/com/ankitsuda/rebound/domain/entities/ExerciseWorkoutJunction.kt @@ -27,6 +27,9 @@ data class ExerciseWorkoutJunction( @ColumnInfo(name = "superset_id") var supersetId: Int? = null, + @ColumnInfo(name = "barbell_id") + var barbellId: String? = null, + @ColumnInfo(name = "exercise_id") var exerciseId: String? = null, @ColumnInfo(name = "workout_id") diff --git a/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt b/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt index 2088f6ac..44b32714 100644 --- a/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt +++ b/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt @@ -49,6 +49,7 @@ const val BARBELL_ID_KEY = "barbell_id" 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" interface Screen { val route: String @@ -500,6 +501,26 @@ sealed class LeafScreen( "superset_selector?$WORKOUT_ID_KEY=$workoutId&$JUNCTION_ID_KEY=$junctionId" } } + + data class BarbellSelector(override val route: String = "barbell_selector?$JUNCTION_ID_KEY={$JUNCTION_ID_KEY}&$BARBELL_ID_KEY={$BARBELL_ID_KEY}") : + LeafScreen( + route = route, + arguments = listOf( + navArgument(BARBELL_ID_KEY) { + type = NavType.StringType + nullable = true + }, + navArgument(JUNCTION_ID_KEY) { + type = NavType.StringType + nullable = false + } + ) + ) { + companion object { + fun createRoute(junctionId: String, selectedBarbellId: String? = null) = + "barbell_selector?$JUNCTION_ID_KEY=$junctionId&$BARBELL_ID_KEY=$selectedBarbellId" + } + } } @OptIn(ExperimentalAnimationApi::class) diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/NumKeysContainerComponent.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/NumKeysContainerComponent.kt index d562a81a..65a21648 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/NumKeysContainerComponent.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/NumKeysContainerComponent.kt @@ -83,7 +83,7 @@ private fun getAllKeys( keys.add(NumberNumKey(value = i)) } - if (reboundKeyboardType == ReboundKeyboardType.WEIGHT || reboundKeyboardType == ReboundKeyboardType.DISTANCE) { + if (reboundKeyboardType is ReboundKeyboardType.Weight || reboundKeyboardType is ReboundKeyboardType.Distance) { keys.add(DecimalNumKey) } else { keys.add(null) diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardComponent.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardComponent.kt index 3188ab56..ffd7bc1f 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardComponent.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardComponent.kt @@ -51,9 +51,9 @@ fun ReboundSetKeyboardComponent( } val currentMode = when (reboundKeyboardType) { - ReboundKeyboardType.WEIGHT -> mCurrentMode - ReboundKeyboardType.WARMUP_SET -> KeyboardModeType.WARMUP_PICKER - ReboundKeyboardType.RPE -> KeyboardModeType.RPE_PICKER + is ReboundKeyboardType.Weight -> mCurrentMode + is ReboundKeyboardType.WarmupSet -> KeyboardModeType.WARMUP_PICKER + is ReboundKeyboardType.Rpe -> KeyboardModeType.RPE_PICKER else -> KeyboardModeType.NUMBERS } @@ -122,13 +122,18 @@ fun ReboundSetKeyboardComponent( ) } KeyboardModeType.PLATE_CALCULATOR -> { - key(LocalAppSettings.current.weightUnit) { + key(LocalAppSettings.current.weightUnit, reboundKeyboardType) { PlateCalculatorComponent( modifier = Modifier .fillMaxWidth() .height( height = 250.dp, ), + barbell = if (reboundKeyboardType is ReboundKeyboardType.Weight) { + reboundKeyboardType.barbell + } else { + null + }, weight = inputConnection?.getText()?.toDoubleOrNull() ?: 0.0 ) } @@ -186,7 +191,7 @@ private fun handleOnClickNumKey( when (numKey) { is NumberNumKey -> commitText() - is DecimalNumKey -> if ((reboundKeyboardType == ReboundKeyboardType.WEIGHT || reboundKeyboardType == ReboundKeyboardType.DISTANCE) && !currentText.contains( + is DecimalNumKey -> if ((reboundKeyboardType is ReboundKeyboardType.Weight || reboundKeyboardType is ReboundKeyboardType.Distance) && !currentText.contains( "." ) ) { diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardDemoScreen.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardDemoScreen.kt index 2e8495cb..9f972278 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardDemoScreen.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardDemoScreen.kt @@ -38,7 +38,7 @@ fun ReboundSetKeyboardDemoScreen() { ReboundSetTextField( contentColor = Color.Black, bgColor = Color.Gray, - reboundKeyboardType = ReboundKeyboardType.WEIGHT, + reboundKeyboardType = ReboundKeyboardType.Weight(), value = weight.toString(), onValueChange = { weight = it.toDoubleOrNull() ?: weight } @@ -49,7 +49,8 @@ fun ReboundSetKeyboardDemoScreen() { modifier = Modifier .fillMaxWidth() .fillMaxHeight(), - weight = weight + weight = weight, + barbell = null, ) } diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardHost.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardHost.kt index 89dff616..07defead 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardHost.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/ReboundSetKeyboardHost.kt @@ -42,8 +42,8 @@ import com.google.accompanist.insets.navigationBarsPadding fun ReboundKeyboardHost(content: @Composable () -> Unit) { val theme = LocalThemeState.current - var reboundSetKeyboardType by remember { - mutableStateOf(ReboundKeyboardType.WEIGHT) + var reboundSetKeyboardType: ReboundKeyboardType by remember { + mutableStateOf(ReboundKeyboardType.Weight()) } var reboundSetKeyboardVisible by remember { mutableStateOf(false) @@ -52,20 +52,18 @@ fun ReboundKeyboardHost(content: @Composable () -> Unit) { mutableStateOf(null) } - val reboundSetKeyboard by remember { - mutableStateOf( - ReboundSetKeyboard( - onChangeKeyboardType = { - reboundSetKeyboardType = it - }, + val reboundSetKeyboard = remember { + ReboundSetKeyboard( + onChangeKeyboardType = { + reboundSetKeyboardType = it + }, - onChangeVisibility = { - reboundSetKeyboardVisible = it - }, - onChangeInputConnection = { - reboundSetKeyboardInputConnection = it - } - ) + onChangeVisibility = { + reboundSetKeyboardVisible = it + }, + onChangeInputConnection = { + reboundSetKeyboardInputConnection = it + } ) } diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/RightLayoutComponent.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/RightLayoutComponent.kt index 455a8cb6..c4190218 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/RightLayoutComponent.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/RightLayoutComponent.kt @@ -53,7 +53,7 @@ internal fun RightLayoutComponent( } } ) { - if (keyboardType == ReboundKeyboardType.WEIGHT) { + if (keyboardType is ReboundKeyboardType.Weight) { IconButton(onClick = { onChangeLayoutMode( if (currentLayoutMode == KeyboardModeType.NUMBERS) { diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/enums/ReboundKeyboardType.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/enums/ReboundKeyboardType.kt index ef7e1141..4b0ff91c 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/enums/ReboundKeyboardType.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/enums/ReboundKeyboardType.kt @@ -14,11 +14,16 @@ package com.ankitsuda.rebound.ui.keyboard.enums -enum class ReboundKeyboardType { - WEIGHT, - REPS, - DISTANCE, - TIME, - WARMUP_SET, - RPE; +import com.ankitsuda.rebound.domain.entities.Barbell + +sealed class ReboundKeyboardType { + data class Weight( + val barbell: Barbell? = null + ) : ReboundKeyboardType() + + object Reps : ReboundKeyboardType() + object Distance : ReboundKeyboardType() + object Time : ReboundKeyboardType() + object WarmupSet : ReboundKeyboardType() + object Rpe : ReboundKeyboardType() } \ No newline at end of file diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/field/ReboundSetTextField.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/field/ReboundSetTextField.kt index 6c307db3..f4114c5b 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/field/ReboundSetTextField.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/field/ReboundSetTextField.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -76,8 +77,7 @@ fun RowScope.ReboundSetTextField( onValueChange?.invoke(newValue) } } - - setRawInputType(InputType.TYPE_CLASS_TEXT) + setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) setTextIsSelectable(true) val ic = onCreateInputConnection(EditorInfo()) diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponent.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponent.kt index 7711005a..8f337210 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponent.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponent.kt @@ -23,14 +23,19 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import com.ankitsuda.base.util.fromKgToLbs +import com.ankitsuda.base.util.fromLbsToKg import com.ankitsuda.base.util.toReadableString +import com.ankitsuda.common.compose.LocalAppSettings +import com.ankitsuda.common.compose.kgToUserPrefStr import com.ankitsuda.common.compose.userPrefWeightUnitStr +import com.ankitsuda.rebound.domain.WeightUnit +import com.ankitsuda.rebound.domain.entities.Barbell import com.ankitsuda.rebound.domain.entities.Plate import com.ankitsuda.rebound.ui.components.RSpacer import com.ankitsuda.rebound.ui.theme.LocalThemeState @@ -41,6 +46,7 @@ import timber.log.Timber fun PlateCalculatorComponent( modifier: Modifier, weight: Double, + barbell: Barbell?, viewModel: PlateCalculatorComponentViewModel = hiltViewModel() ) { val plates by viewModel.plates.collectAsState(emptyList()) @@ -48,6 +54,10 @@ fun PlateCalculatorComponent( val theme = LocalThemeState.current + LaunchedEffect(key1 = barbell) { + viewModel.updateBarbell(barbell) + } + LaunchedEffect(key1 = weight) { viewModel.refreshPlates(weight) } @@ -76,6 +86,7 @@ fun PlateCalculatorComponent( modifier = Modifier .fillMaxWidth() .weight(1f), + barbell = barbell, plates = plates ) @@ -87,6 +98,7 @@ fun PlateCalculatorComponent( @Composable private fun BarbellComponent( modifier: Modifier, + barbell: Barbell?, plates: List ) { val theme = LocalThemeState.current @@ -95,7 +107,10 @@ private fun BarbellComponent( val barbellColor = theme.keyboardBarbellColor val onBarbellColor = theme.keyboardOnBarbellColor - val barbellWeight = 0F + val barbellWeight = when (LocalAppSettings.current.weightUnit) { + WeightUnit.LBS -> barbell?.weightLbs ?: barbell?.weightKg?.fromKgToLbs() ?: 0.0 + else -> barbell?.weightKg ?: barbell?.weightLbs?.fromLbsToKg() ?: 0.0 + } val scrollState = rememberScrollState() diff --git a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponentViewModel.kt b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponentViewModel.kt index 7835a8f4..258d2c46 100644 --- a/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponentViewModel.kt +++ b/modules/ui-keyboard/src/main/java/com/ankitsuda/rebound/ui/keyboard/platecalculator/PlateCalculatorComponentViewModel.kt @@ -16,8 +16,12 @@ package com.ankitsuda.rebound.ui.keyboard.platecalculator import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.ankitsuda.base.util.fromKgToLbs +import com.ankitsuda.base.util.fromLbsToKg import com.ankitsuda.rebound.data.datastore.AppPreferences import com.ankitsuda.rebound.data.repositories.PlatesRepository +import com.ankitsuda.rebound.domain.WeightUnit +import com.ankitsuda.rebound.domain.entities.Barbell import com.ankitsuda.rebound.domain.entities.Plate import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job @@ -42,12 +46,15 @@ class PlateCalculatorComponentViewModel @Inject constructor( private var platesJob: Job? = null private var lastWeight: Double? = null + private var weightUnit: WeightUnit? = null private val _allPlates = arrayListOf() + private var barbell: Barbell? = null init { viewModelScope.launch { prefs.weightUnit.collectLatest { unit -> + weightUnit = unit platesRepository.getActivePlates(forWeightUnit = unit).collectLatest { _allPlates.clear() _allPlates.addAll(it) @@ -59,15 +66,29 @@ class PlateCalculatorComponentViewModel @Inject constructor( } } + fun updateBarbell(newBarbell: Barbell?) { + barbell = newBarbell + if (lastWeight != null) { + refreshPlates(lastWeight!!) + } + } + fun refreshPlates(newWeight: Double) { platesJob?.cancel() platesJob = viewModelScope.launch { + val barbellWeight = when (weightUnit) { + WeightUnit.LBS -> barbell?.weightLbs ?: barbell?.weightKg?.fromKgToLbs() ?: 0.0 + else -> barbell?.weightKg ?: barbell?.weightLbs?.fromLbsToKg() ?: 0.0 + } + + val weightForPlates = newWeight - barbellWeight + val platesNeeded = - calculatePlates(newWeight, _allPlates.sortedByDescending { it.weight }) + calculatePlates(weightForPlates, _allPlates.sortedByDescending { it.weight }) val sumOfPlates = platesNeeded.sumOf { it.weight ?: 0.0 } _plates.value = platesNeeded _remainingWeight.value = try { - newWeight - sumOfPlates * 2 + weightForPlates - sumOfPlates * 2 } catch (e: Exception) { e.printStackTrace() 0.0 diff --git a/modules/ui-workout-edit/src/main/java/com/ankitsuda/rebound/ui/workoutedit/WorkoutEditScreen.kt b/modules/ui-workout-edit/src/main/java/com/ankitsuda/rebound/ui/workoutedit/WorkoutEditScreen.kt index ad1984ba..b752cd2f 100644 --- a/modules/ui-workout-edit/src/main/java/com/ankitsuda/rebound/ui/workoutedit/WorkoutEditScreen.kt +++ b/modules/ui-workout-edit/src/main/java/com/ankitsuda/rebound/ui/workoutedit/WorkoutEditScreen.kt @@ -104,6 +104,7 @@ fun WorkoutEditScreen( useReboundKeyboard = true, cancelWorkoutButtonVisible = false, logEntriesWithJunction = logEntriesWithJunction, + barbells = emptyList(), // TODO: Fix it onChangeWorkoutName = { viewModel.updateWorkoutName(it) }, @@ -137,6 +138,7 @@ fun WorkoutEditScreen( onDeleteNote = viewModel::deleteNote, onAddToSuperset = viewModel::addToSuperset, onRemoveFromSuperset = viewModel::removeFromSuperset, + onUpdateBarbell = { _, _ -> } // TODO: Fix it ) } diff --git a/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanel.kt b/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanel.kt index 03c87a83..f393b3f5 100644 --- a/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanel.kt +++ b/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanel.kt @@ -60,7 +60,7 @@ fun WorkoutPanel( WorkoutPanel1(navController, navigator) } -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class) +@OptIn(ExperimentalFoundationApi::class) @Composable fun WorkoutPanel1( navController: NavHostController, @@ -73,9 +73,8 @@ fun WorkoutPanel1( val currentVolumeStr by viewModel.currentVolumeStr.collectAsState("") val currentSetsStr by viewModel.currentSetsStr.collectAsState("") val logEntriesWithJunction by viewModel.logEntriesWithExerciseJunction.collectAsState() + val barbells by viewModel.barbells.collectAsState(emptyList()) -// val workoutName by viewModel.workoutName.collectAsState("") -// val workoutNote by viewModel.workoutNote.collectAsState("") val workoutName = workout?.name val workoutNote = workout?.note @@ -89,6 +88,7 @@ fun WorkoutPanel1( addNavigationBarPadding = true, cancelWorkoutButtonVisible = true, logEntriesWithJunction = logEntriesWithJunction, + barbells = barbells, layoutAtTop = { Column( modifier = Modifier.animateItemPlacement() @@ -136,6 +136,7 @@ fun WorkoutPanel1( onDeleteNote = viewModel::deleteNote, onAddToSuperset = viewModel::addToSuperset, onRemoveFromSuperset = viewModel::removeFromSuperset, + onUpdateBarbell = viewModel::updateExerciseBarbellType ) } } diff --git a/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanelViewModel.kt b/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanelViewModel.kt index e6036b2c..cbec5a5a 100644 --- a/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanelViewModel.kt +++ b/modules/ui-workout-panel/src/main/java/com/ankitsuda/rebound/ui/workout_panel/WorkoutPanelViewModel.kt @@ -18,9 +18,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ankitsuda.base.util.NONE_WORKOUT_ID import com.ankitsuda.base.util.toReadableString +import com.ankitsuda.base.utils.extensions.shareWhileObserved import com.ankitsuda.base.utils.extensions.toArrayList import com.ankitsuda.base.utils.generateId import com.ankitsuda.base.utils.toReadableDurationStyle2 +import com.ankitsuda.rebound.data.repositories.BarbellsRepository import com.ankitsuda.rebound.data.repositories.WorkoutsRepository import com.ankitsuda.rebound.domain.ExerciseCategory import com.ankitsuda.rebound.domain.LogSetType @@ -35,7 +37,10 @@ import java.time.LocalDateTime import javax.inject.Inject @HiltViewModel -class WorkoutPanelViewModel @Inject constructor(private val workoutsRepository: WorkoutsRepository) : +class WorkoutPanelViewModel @Inject constructor( + barbellsRepository: BarbellsRepository, + private val workoutsRepository: WorkoutsRepository, +) : ViewModel() { val currentWorkoutId: Flow = workoutsRepository.getCurrentWorkoutId() var mWorkoutId: Long = -1 @@ -75,9 +80,10 @@ class WorkoutPanelViewModel @Inject constructor(private val workoutsRepository: private var _logEntriesWithExerciseJunction: MutableStateFlow> = MutableStateFlow(emptyList()) val logEntriesWithExerciseJunction = _logEntriesWithExerciseJunction.asStateFlow() -// private var _logEntriesWithExerciseJunction: SnapshotStateList = -// SnapshotStateList() -// val logEntriesWithExerciseJunction = _logEntriesWithExerciseJunction + + val barbells = barbellsRepository.getActiveBarbells() + .distinctUntilChanged() + .shareWhileObserved(viewModelScope) init { viewModelScope.launch { @@ -307,4 +313,12 @@ class WorkoutPanelViewModel @Inject constructor(private val workoutsRepository: } } + fun updateExerciseBarbellType(junctionId: String, barbellId: String) { + viewModelScope.launch { + workoutsRepository.updateExerciseWorkoutJunctionBarbellId( + junctionId, barbellId + ) + } + } + } \ No newline at end of file