Skip to content

Commit

Permalink
feat(android): score editor sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
autoreleasefool committed Feb 15, 2024
1 parent 5a99391 commit 97e6112
Show file tree
Hide file tree
Showing 17 changed files with 353 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ca.josephroque.bowlingcompanion.core.navigation.popBackStackWithResult
import ca.josephroque.bowlingcompanion.feature.accessoriesoverview.navigation.accessoriesOnboardingSheet
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.gamesSettingsScreen
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.navigateToGamesEditor
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.scoreEditorScreen
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.scoresListScreen
import ca.josephroque.bowlingcompanion.feature.matchplayeditor.navigation.matchPlayEditorScreen
import ca.josephroque.bowlingcompanion.feature.overview.navigation.navigateToQuickPlayOnboarding
Expand Down Expand Up @@ -111,4 +112,7 @@ fun NavGraphBuilder.bottomSheetGraph(navController: NavController) {
)
},
)
scoreEditorScreen(
onDismissWithResult = navController::popBackStackWithResult,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ca.josephroque.bowlingcompanion.feature.bowlerform.navigation.navigateToN
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.gamesEditorScreen
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.navigateToGamesEditor
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.navigateToGamesSettingsForResult
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.navigateToScoreEditorForResult
import ca.josephroque.bowlingcompanion.feature.gameseditor.navigation.navigateToScoresList
import ca.josephroque.bowlingcompanion.feature.laneform.navigation.laneFormScreen
import ca.josephroque.bowlingcompanion.feature.leaguedetails.navigation.leagueDetailsScreen
Expand Down Expand Up @@ -172,6 +173,13 @@ fun NavGraphBuilder.overviewGraph(
},
onShowStatistics = { args -> navController.navigateToMidGameStatisticsDetails(args.filter) },
onShowBowlerScores = { args -> navController.navigateToScoresList(args.gameIndex, args.series) },
onEditScore = { args ->
navController.navigateToScoreEditorForResult(
args.scoringMethod,
args.score,
args.onScoreUpdated,
)
},
)
statisticsWidgetLayoutEditorScreen(
onBackPressed = navController::popBackStack,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ca.josephroque.bowlingcompanion.core.navigation
import android.net.Uri
import androidx.lifecycle.SavedStateHandle
import ca.josephroque.bowlingcompanion.core.model.BowlerKind
import ca.josephroque.bowlingcompanion.core.model.GameScoringMethod
import ca.josephroque.bowlingcompanion.core.model.ResourcePickerType
import ca.josephroque.bowlingcompanion.core.model.StatisticsDetailsSourceType
import ca.josephroque.bowlingcompanion.core.statistics.StatisticID
Expand Down Expand Up @@ -115,6 +116,15 @@ sealed class Route(
fun getGameIndex(savedStateHandle: SavedStateHandle): Int? =
savedStateHandle.get<Int>("gameIndex")
}
data object ScoreEditor : Route("edit_score/{scoringMethod}/{score}", isBottomBarVisible = false) {
const val ARG_SCORING_METHOD = "scoringMethod"
const val ARG_SCORE = "score"
fun createRoute(scoringMethod: GameScoringMethod, score: Int): String =
"edit_score/${Uri.encode(scoringMethod.name)}/$score"
fun getScoringMethod(savedStateHandle: SavedStateHandle) =
savedStateHandle.get<GameScoringMethod>("scoringMethod")
fun getScore(savedStateHandle: SavedStateHandle) = savedStateHandle.get<Int>("score")
}

// Gear
data object GearList : Route("gear")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ internal fun GamesEditorRoute(
onShowGamesSettings: (GamesEditorArguments.ShowGamesSettings) -> Unit,
onShowStatistics: (GamesEditorArguments.ShowStatistics) -> Unit,
onShowBowlerScores: (GamesEditorArguments.ShowBowlerScores) -> Unit,
onEditScore: (GamesEditorArguments.EditScore) -> Unit,
modifier: Modifier = Modifier,
viewModel: GamesEditorViewModel = hiltViewModel(),
) {
Expand Down Expand Up @@ -109,6 +110,12 @@ internal fun GamesEditorRoute(
onShowStatistics(GamesEditorArguments.ShowStatistics(it.filter))
is GamesEditorScreenEvent.ShowBowlerScores ->
onShowBowlerScores(GamesEditorArguments.ShowBowlerScores(it.series, it.gameIndex))
is GamesEditorScreenEvent.EditScore ->
onEditScore(
GamesEditorArguments.EditScore(it.score, it.scoringMethod) { result ->
viewModel.handleAction(GamesEditorScreenUiAction.ScoreUpdated(result.second, result.first))
},
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ca.josephroque.bowlingcompanion.feature.gameseditor

import ca.josephroque.bowlingcompanion.core.model.GameScoringMethod
import ca.josephroque.bowlingcompanion.core.model.TrackableFilter
import ca.josephroque.bowlingcompanion.core.navigation.NavResultCallback
import ca.josephroque.bowlingcompanion.feature.gameseditor.ui.GamesEditorUiAction
Expand Down Expand Up @@ -38,6 +39,12 @@ object GamesEditorArguments {
val onSeriesUpdated: NavResultCallback<Pair<List<UUID>, UUID>>,
)

data class EditScore(
val score: Int,
val scoringMethod: GameScoringMethod,
val onScoreUpdated: NavResultCallback<Pair<GameScoringMethod, Int>>,
)

data class ShowStatistics(val filter: TrackableFilter)

data class ShowBowlerScores(val series: List<UUID>, val gameIndex: Int)
Expand Down Expand Up @@ -67,6 +74,10 @@ sealed interface GamesEditorScreenUiAction {
data class SeriesUpdated(val series: List<UUID>) : GamesEditorScreenUiAction
data class CurrentGameUpdated(val gameId: UUID) : GamesEditorScreenUiAction
data class SelectedBallUpdated(val ballId: UUID?) : GamesEditorScreenUiAction
data class ScoreUpdated(
val score: Int,
val scoringMethod: GameScoringMethod,
) : GamesEditorScreenUiAction
}

sealed interface GamesEditorScreenEvent {
Expand All @@ -81,6 +92,7 @@ sealed interface GamesEditorScreenEvent {
val series: List<UUID>,
val currentGameId: UUID,
) : GamesEditorScreenEvent
data class EditScore(val score: Int, val scoringMethod: GameScoringMethod) : GamesEditorScreenEvent
data class ShowStatistics(val filter: TrackableFilter) : GamesEditorScreenEvent
data class ShowBowlerScores(val series: List<UUID>, val gameIndex: Int) : GamesEditorScreenEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import ca.josephroque.bowlingcompanion.core.data.repository.ScoresRepository
import ca.josephroque.bowlingcompanion.core.data.repository.SeriesRepository
import ca.josephroque.bowlingcompanion.core.model.ExcludeFromStatistics
import ca.josephroque.bowlingcompanion.core.model.FrameEdit
import ca.josephroque.bowlingcompanion.core.model.Game
import ca.josephroque.bowlingcompanion.core.model.GameLockState
import ca.josephroque.bowlingcompanion.core.model.GameScoringMethod
import ca.josephroque.bowlingcompanion.core.model.GearKind
Expand All @@ -40,10 +39,7 @@ import ca.josephroque.bowlingcompanion.feature.gameseditor.ui.gamedetails.GameDe
import ca.josephroque.bowlingcompanion.feature.gameseditor.ui.gamedetails.GameDetailsUiState
import ca.josephroque.bowlingcompanion.feature.gameseditor.ui.gamedetails.NextGameEditableElement
import ca.josephroque.bowlingcompanion.feature.gameseditor.ui.rolleditor.RollEditorUiAction
import ca.josephroque.bowlingcompanion.feature.gameseditor.ui.scoreeditor.ScoreEditorUiAction
import ca.josephroque.bowlingcompanion.feature.gameseditor.ui.scoreeditor.ScoreEditorUiState
import ca.josephroque.bowlingcompanion.feature.gameseditor.utils.ensureRollExists
import ca.josephroque.bowlingcompanion.feature.gameseditor.utils.getAndUpdateGamesEditor
import ca.josephroque.bowlingcompanion.feature.gameseditor.utils.selectedFrame
import ca.josephroque.bowlingcompanion.feature.gameseditor.utils.setBallRolled
import ca.josephroque.bowlingcompanion.feature.gameseditor.utils.setDidFoul
Expand Down Expand Up @@ -156,6 +152,7 @@ class GamesEditorViewModel @Inject constructor(
is GamesEditorScreenUiAction.SeriesUpdated -> updateSeries(action.series)
is GamesEditorScreenUiAction.CurrentGameUpdated -> loadGameIfChanged(action.gameId)
is GamesEditorScreenUiAction.SelectedBallUpdated -> updateSelectedBall(id = action.ballId)
is GamesEditorScreenUiAction.ScoreUpdated -> updateScore(action.score, action.scoringMethod)
}
}

Expand Down Expand Up @@ -186,7 +183,6 @@ class GamesEditorViewModel @Inject constructor(
is GamesEditorUiAction.FrameEditor -> handleFrameEditorAction(action.action)
is GamesEditorUiAction.RollEditor -> handleRollEditorAction(action.action)
is GamesEditorUiAction.ScoreSheet -> handleScoreSheetAction(action.action)
is GamesEditorUiAction.ScoreEditor -> handleScoreEditorAction(action.action)
}
}

Expand All @@ -213,17 +209,6 @@ class GamesEditorViewModel @Inject constructor(
}
}

private fun handleScoreEditorAction(action: ScoreEditorUiAction) {
when (action) {
ScoreEditorUiAction.CancelClicked -> dismissScoreEditor(didSave = false)
ScoreEditorUiAction.SaveClicked -> dismissScoreEditor(didSave = true)
is ScoreEditorUiAction.ScoreChanged -> updateScoreEditorScore(score = action.score)
is ScoreEditorUiAction.ScoringMethodChanged -> updateScoreEditorScoringMethod(
score = action.scoringMethod,
)
}
}

private fun updateSeries(series: List<UUID>) {
this.series.update { series }
}
Expand Down Expand Up @@ -562,14 +547,12 @@ class GamesEditorViewModel @Inject constructor(

isGameDetailsSheetVisible.value = false
val gameDetails = gameDetailsState.value
gamesEditorState.updateGamesEditor(gameDetails.gameId) {
it.copy(
scoreEditor = ScoreEditorUiState(
score = gameDetails.scoringMethod.score,
scoringMethod = gameDetails.scoringMethod.scoringMethod,
),
)
}
sendEvent(
GamesEditorScreenEvent.EditScore(
score = gameDetails.scoringMethod.score,
scoringMethod = gameDetails.scoringMethod.scoringMethod,
),
)
}

private fun toggleGameLocked(isLocked: Boolean) {
Expand Down Expand Up @@ -808,62 +791,41 @@ class GamesEditorViewModel @Inject constructor(
saveFrame(gamesEditorState.selectedFrame())
}

private fun updateScoreEditorScore(score: String) {
gamesEditorState.update {
it.copy(
scoreEditor = it.scoreEditor?.copy(
score = score.toIntOrNull()?.coerceIn(0, Game.MAX_SCORE) ?: 0,
),
)
}
}

private fun updateScoreEditorScoringMethod(score: GameScoringMethod) {
gamesEditorState.update {
it.copy(
scoreEditor = it.scoreEditor?.copy(
scoringMethod = score,
),
)
private fun updateScore(score: Int, scoringMethod: GameScoringMethod) {
if (isGameLocked) {
notifyGameLocked()
return
}
}

private fun dismissScoreEditor(didSave: Boolean) {
val gameId = currentGameId.value
val gamesEditorState = gamesEditorState.getAndUpdateGamesEditor(gameId) {
it.copy(scoreEditor = null)
}

val scoreEditor = gamesEditorState.scoreEditor ?: return
if (didSave && !isGameLocked) {
when (scoreEditor.scoringMethod) {
if (scoringMethod != gameDetailsState.value.scoringMethod.scoringMethod) {
when (scoringMethod) {
GameScoringMethod.MANUAL -> analyticsClient.trackEvent(GameManualScoreSet(eventId = gameId))
GameScoringMethod.BY_FRAME -> Unit
}
}

this.gamesEditorState.updateGamesEditor(gameId) {
it.copy(
manualScore = when (scoreEditor.scoringMethod) {
GameScoringMethod.MANUAL -> scoreEditor.score
GameScoringMethod.BY_FRAME -> null
},
)
}
gameDetailsState.updateGameDetails(gameId) {
it.copy(
scoringMethod = it.scoringMethod.copy(
score = score,
scoringMethod = scoringMethod,
),
)
}

viewModelScope.launch {
val score = when (scoreEditor.scoringMethod) {
GameScoringMethod.MANUAL -> scoreEditor.score
GameScoringMethod.BY_FRAME -> scoresRepository.getScore(
gamesEditorState.gameId,
).first().score ?: 0
}
gamesEditorState.updateGamesEditor(gameId) {
it.copy(
manualScore = when (scoringMethod) {
GameScoringMethod.MANUAL -> score
GameScoringMethod.BY_FRAME -> null
},
)
}

gamesRepository.setGameScoringMethod(
gamesEditorState.gameId,
scoreEditor.scoringMethod,
score,
)
}
viewModelScope.launch {
gamesRepository.setGameScoringMethod(gameId, scoringMethod, score)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fun NavGraphBuilder.gamesEditorScreen(
onShowGamesSettings: (GamesEditorArguments.ShowGamesSettings) -> Unit,
onShowStatistics: (GamesEditorArguments.ShowStatistics) -> Unit,
onShowBowlerScores: (GamesEditorArguments.ShowBowlerScores) -> Unit,
onEditScore: (GamesEditorArguments.EditScore) -> Unit,
) {
composable(
route = Route.EditGame.route,
Expand All @@ -47,6 +48,7 @@ fun NavGraphBuilder.gamesEditorScreen(
onEditRolledBall = onEditRolledBall,
onShowStatistics = onShowStatistics,
onShowBowlerScores = onShowBowlerScores,
onEditScore = onEditScore,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ca.josephroque.bowlingcompanion.feature.gameseditor.navigation

import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.NavType
import androidx.navigation.navArgument
import ca.josephroque.bowlingcompanion.core.model.GameScoringMethod
import ca.josephroque.bowlingcompanion.core.navigation.NavResultCallback
import ca.josephroque.bowlingcompanion.core.navigation.Route
import ca.josephroque.bowlingcompanion.core.navigation.navigateForResult
import ca.josephroque.bowlingcompanion.feature.gameseditor.scoreeditor.ScoreEditorRoute
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.navigation.material.bottomSheet

fun NavController.navigateToScoreEditorForResult(
scoringMethod: GameScoringMethod,
score: Int,
navResultCallback: NavResultCallback<Pair<GameScoringMethod, Int>>,
navOptions: NavOptions? = null,
) {
this.navigateForResult(
route = Route.ScoreEditor.createRoute(scoringMethod, score),
navResultCallback = navResultCallback,
navOptions = navOptions,
)
}

@OptIn(ExperimentalMaterialNavigationApi::class)
fun NavGraphBuilder.scoreEditorScreen(onDismissWithResult: (Pair<GameScoringMethod, Int>) -> Unit) {
bottomSheet(
route = Route.ScoreEditor.route,
arguments = listOf(
navArgument(Route.ScoreEditor.ARG_SCORING_METHOD) {
type = NavType.EnumType(GameScoringMethod::class.java)
},
navArgument(Route.ScoreEditor.ARG_SCORE) { type = NavType.IntType },
),
) {
ScoreEditorRoute(onDismissWithResult = onDismissWithResult)
}
}
Loading

0 comments on commit 97e6112

Please sign in to comment.