Skip to content

Commit

Permalink
Merge pull request #38 from s2g090123/37-add-some-code-comments
Browse files Browse the repository at this point in the history
New: Adding comments
  • Loading branch information
s2g090123 authored Jan 12, 2024
2 parents ae90b8a + 741d7ab commit ab39325
Show file tree
Hide file tree
Showing 66 changed files with 1,202 additions and 114 deletions.
6 changes: 6 additions & 0 deletions app/src/main/java/com/jiachian/nbatoday/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class MainActivity : ComponentActivity() {
toastEventManager.eventFlow.collectWithLifecycle(this::onToastEvent)
}

/**
* Handles navigation events received from the ViewModel.
*/
private fun onNavigationEvent(event: NavigationController.Event?) {
runOnUiThread {
when (event) {
Expand Down Expand Up @@ -102,6 +105,9 @@ class MainActivity : ComponentActivity() {
viewModel.consumeNavigationEvent(event)
}

/**
* Handles toast events received from the toast event manager.
*/
private fun onToastEvent(event: ToastEvent?) {
when (event) {
ToastEvent.OnError -> showErrorToast()
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/jiachian/nbatoday/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import org.koin.core.context.startKoin
class MainApplication : Application() {

companion object {
private var instance: Application? = null
private lateinit var instance: Application
val context: Context
get() = instance!!.applicationContext
get() = instance.applicationContext
}

override fun onCreate() {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/jiachian/nbatoday/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ class MainViewModel(
loadData()
}

/**
* Loads data asynchronously.
* 1. update schedules
* 2. update teams
* 3. update theme colors
* 4. log in the user (if available)
*
* Finally, navigate to the home screen after loading data
*/
private fun loadData() {
viewModelScope.launch(dispatcherProvider.io) {
val updateScheduleDeferred = async {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.jiachian.nbatoday.annotation

/**
* This annotation is used to mark functions or classes that should be excluded from code coverage analysis
* performed by Jacoco. It is particularly useful when you want to skip specific parts of your codebase
* from being included in the coverage report.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
annotation class ExcludeFromJacocoGeneratedReport
12 changes: 12 additions & 0 deletions app/src/main/java/com/jiachian/nbatoday/compose/coil/SvgRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ import android.content.Context
import coil.decode.SvgDecoder
import coil.request.ImageRequest

/**
* A builder class for creating [ImageRequest] instances specifically for loading SVG images using Coil.
*
* This class provides a convenient way to construct [ImageRequest] instances tailored for SVG images.
*
* @usage:
* AsyncImage(
* model = SvgRequest.Builder(LocalContext.current)
* .data(`svg data source`)
* .build()
* )
*/
class SvgRequest private constructor() {
class Builder(context: Context) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ import com.jiachian.nbatoday.navigation.NavigationController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel

/**
* Base ViewModel for Composable in this App.
*
* @property coroutineScope The coroutine scope associated with the ViewModel.
* @property navigationController The navigation controller responsible for screen navigation.
* @property route The route representing the current screen, nullable if not applicable.
*/
open class ComposeViewModel(
protected val coroutineScope: CoroutineScope,
protected val navigationController: NavigationController,
private val route: MainRoute?,
) {
/**
* Closes the current screen by canceling the coroutine scope and navigating back if a route is specified.
*/
open fun close() {
coroutineScope.cancel()
route?.also {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private fun BetDialogDetail(
homePoints: Long,
awayPoints: Long
) {
val remainedPoints by viewModel.remainedPoints.collectAsState()
val remainingPoint by viewModel.remainingPoints.collectAsState()
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
Expand Down Expand Up @@ -146,7 +146,7 @@ private fun BetDialogDetail(
modifier = Modifier
.testTag(BetTestTag.BetDialogDetail_Text_Remainder)
.padding(top = 16.dp, start = 16.dp, end = 16.dp),
text = stringResource(R.string.bet_remain, remainedPoints),
text = stringResource(R.string.bet_remain, remainingPoint),
color = MaterialTheme.colors.primary,
fontSize = 12.sp,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,42 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/**
* ViewModel for handling business logic related to [BetDialog].
*
* @property gameAndBets The data class containing information about the game and associated bets.
* @property userPoints The total points of the user available for betting.
* @property dispatcherProvider The provider for obtaining dispatchers for coroutines (default is [DefaultDispatcherProvider]).
* @property coroutineScope The coroutine scope for managing coroutines (default is [CoroutineScope] with unconfined dispatcher).
*/
class BetDialogViewModel(
val gameAndBets: GameAndBets,
val userPoints: Long,
dispatcherProvider: DispatcherProvider = DefaultDispatcherProvider,
coroutineScope: CoroutineScope = CoroutineScope(dispatcherProvider.unconfined),
) {
// the warning state in the UI
private val warningImp = MutableStateFlow(false)
val warning = warningImp.asStateFlow()

// the selected home points in the UI
private val homePointsImp = MutableStateFlow(0L)
val homePoints = homePointsImp.asStateFlow()

// the selected away points in the UI
private val awayPointsImp = MutableStateFlow(0L)
val awayPoints = awayPointsImp.asStateFlow()

val remainedPoints = combine(
// the remaining points available for betting
val remainingPoints = combine(
homePoints,
awayPoints
) { home, away ->
userPoints - home - away
}.stateIn(coroutineScope, SharingStarted.Lazily, userPoints)

val enabled = remainedPoints.map { points ->
// Determine if the bet button should be enabled based on [remainingPoints].
val enabled = remainingPoints.map { points ->
points != userPoints && points >= 0
}.stateIn(coroutineScope, SharingStarted.Lazily, false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ private const val ThirdSectorMaxAngle = 269f

private const val MaxMagnification = 4

/**
* ViewModel for handling business logic related to [BetScreen].
*
* @property account The user account associated with the logged-in user.
* @property repository The repository for interacting with [BetAndGame].
* @property navigationController The controller for navigation within the app.
* @property dispatcherProvider The provider for obtaining dispatchers for coroutines (default is [DefaultDispatcherProvider]).
* @property coroutineScope The coroutine scope for managing coroutines (default is [CoroutineScope] with unconfined dispatcher).
*/
class BetViewModel(
account: String,
private val repository: BetRepository,
Expand All @@ -52,26 +61,37 @@ class BetViewModel(
navigationController = navigationController,
route = MainRoute.Bet
) {
// state representing the stats of [BetAndGame]
val betsAndGamesState = repository
.getBetsAndGames(account)
.map { UIState.Loaded(it) }
.stateIn(coroutineScope, SharingStarted.Lazily, UIState.Loading())

// the current points on the turn table
private val turnTablePointsImp = MutableStateFlow<TurnTablePoints?>(null)
val turnTablePoints = turnTablePointsImp.asStateFlow()

// whether the turn table is currently running
private val turnTableRunningImp = MutableStateFlow(false)
val turnTableRunning = turnTableRunningImp.asStateFlow()

// the current angle of the turn table
private val turnTableAngleImp = MutableStateFlow(0f)
val turnTableAngle = turnTableAngleImp.asStateFlow()

// the visibility of the turn table
private val turnTableVisibleImp = MutableStateFlow(false)
val turnTableVisible = turnTableVisibleImp.asStateFlow()

// the points rewarded after the turn table animation
private val rewardedPointsImp = MutableStateFlow<Long?>(null)
val rewardedPoints = rewardedPointsImp.asStateFlow()

/**
* Handles the click event on a specific [BetAndGame].
*
* @param betAndGame The clicked [BetAndGame].
*/
fun clickBetAndGame(betAndGame: BetAndGame) {
when (betAndGame.game.gameStatus) {
GameStatus.COMING_SOON -> {
Expand Down Expand Up @@ -107,6 +127,11 @@ class BetViewModel(
turnTableVisibleImp.value = true
}

/**
* Starts the turn table animation with the specified points configuration.
*
* @param turnTablePoints The points configuration for the turn table.
*/
fun startTurnTable(turnTablePoints: TurnTablePoints) {
coroutineScope.launch(dispatcherProvider.io) {
turnTableRunningImp.value = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

/**
* ViewModel for handling business logic related to [CalendarScreen].
*
* @property dateTime The initial date and time for the calendar.
* @property repository The repository for interacting with [GameAndBets].
* @property navigationController The controller for navigation within the app.
* @property composeViewModelProvider The provider for creating ComposeViewModel instances.
* @property dispatcherProvider The provider for obtaining dispatchers for coroutines (default is [DefaultDispatcherProvider]).
* @property coroutineScope The coroutine scope for managing coroutines (default is [CoroutineScope] with unconfined dispatcher).
*/
class CalendarViewModel(
dateTime: Long,
private val repository: GameRepository,
Expand All @@ -48,19 +58,23 @@ class CalendarViewModel(
private val selectedDateImp = MutableStateFlow(Date(dateTime))
val selectedDate = selectedDateImp.asStateFlow()

// the list of games for the selected date
private val selectedGamesImp = MutableStateFlow(emptyList<GameAndBets>())
val selectedGames = selectedGamesImp.asStateFlow()

// the loading status of [selectedGames]
private val loadingGamesImp = MutableStateFlow(false)
val loadingGames = loadingGamesImp.asStateFlow()

// the loading status of [calendarDates]
private val loadingCalendar = MutableStateFlow(false)

private val lastGameDate = repository.getLastGameDateTime()
.stateIn(coroutineScope, SharingStarted.Lazily, Date(dateTime))
private val firstGameDate = repository.getFirstGameDateTime()
.stateIn(coroutineScope, SharingStarted.Lazily, Date(dateTime))

// Initialize the [currentCalendar] based on the provided date and time.
init {
currentCalendar = DateUtils.getCalendar().let {
it.timeInMillis = dateTime
Expand Down Expand Up @@ -147,6 +161,9 @@ class CalendarViewModel(
selectedDateImp.value = date
}

/**
* Moves to the next month in the calendar.
*/
fun nextMonth() {
if (hasNextMonth()) {
currentCalendar.value = DateUtils.getCalendar().apply {
Expand All @@ -156,6 +173,9 @@ class CalendarViewModel(
}
}

/**
* Moves to the previous month in the calendar.
*/
fun lastMonth() {
if (hasLastMonth()) {
currentCalendar.value = DateUtils.getCalendar().apply {
Expand All @@ -165,6 +185,12 @@ class CalendarViewModel(
}
}

/**
* Retrieves the list of dates for the current calendar month.
*
* @param calendar The calendar instance representing the current month.
* @return List of CalendarDate objects representing each day in the month.
*/
private fun getCalendarDates(calendar: Calendar): List<CalendarDate> {
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
Expand Down Expand Up @@ -200,6 +226,11 @@ class CalendarViewModel(
}
}

/**
* Click event handler for a game card.
*
* @param game The selected game.
*/
fun clickGameCard(game: Game) {
if (game.gamePlayed) {
navigationController.navigateToBoxScore(game.gameId)
Expand Down
Loading

0 comments on commit ab39325

Please sign in to comment.