Skip to content

Commit

Permalink
Merge pull request #33 from stslex/dev
Browse files Browse the repository at this point in the history
dev
  • Loading branch information
stslex authored Apr 28, 2024
2 parents 3aa8d4c + 6e18868 commit 91c3551
Show file tree
Hide file tree
Showing 20 changed files with 436 additions and 161 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.stslex.core.core.coroutine

import com.stslex.core.core.AppDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow

interface AppCoroutineScope {

/**
* Launches a coroutine and catches exceptions. The coroutine is launched on the default dispatcher of the AppDispatcher.
* @param onError - error handler
* @param onSuccess - success handler
* @param action - action to be executed
* @return Job
* @see Job
* @see AppDispatcher
* */
fun <T> launch(
onError: suspend (Throwable) -> Unit = {},
onSuccess: suspend CoroutineScope.(T) -> Unit = {},
action: suspend CoroutineScope.() -> T,
): Job

/**
* Launches a flow and collects it in the screenModelScope. The flow is collected on the default dispatcher. of the AppDispatcher.
* @param onError - error handler
* @param each - action for each element of the flow
* @return Job
* @see Flow
* @see Job
* @see AppDispatcher
* */
fun <T> launch(
flow: Flow<T>,
onError: suspend (cause: Throwable) -> Unit = {},
each: suspend (T) -> Unit
): Job
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.stslex.core.core.coroutine

import com.stslex.core.core.AppDispatcher
import com.stslex.core.core.Logger
import com.stslex.core.core.coroutineExceptionHandler
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch

class AppCoroutineScopeImpl(
private val scope: CoroutineScope,
private val appDispatcher: AppDispatcher
) : AppCoroutineScope {

private fun exceptionHandler(
onError: suspend (cause: Throwable) -> Unit = {},
) = CoroutineExceptionHandler { _, throwable ->
Logger.exception(throwable)
scope.launch(appDispatcher.default + coroutineExceptionHandler) {
onError(throwable)
}
}

override fun <T> launch(
onError: suspend (Throwable) -> Unit,
onSuccess: suspend CoroutineScope.(T) -> Unit,
action: suspend CoroutineScope.() -> T
): Job = scope.launch(
context = exceptionHandler(onError) + appDispatcher.default,
block = {
onSuccess(action())
}
)

override fun <T> launch(
flow: Flow<T>,
onError: suspend (cause: Throwable) -> Unit,
each: suspend (T) -> Unit
): Job = scope.launch(
context = exceptionHandler(onError) + appDispatcher.default,
block = {
flow.collect(each)
}
)
}
63 changes: 34 additions & 29 deletions core/ui/src/commonMain/kotlin/com/stslex/core/ui/base/Animations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,42 +29,47 @@ import com.stslex.core.ui.theme.toPx
import kotlin.math.roundToInt

fun Modifier.shimmerLoadingAnimation(
isShimmerVisible: Boolean = true,
widthOfShadowBrush: Int = 500,
angleOfAxisY: Float = 270f,
durationMillis: Int = 1000,
): Modifier = composed {
val shimmerColors = with(MaterialTheme.colorScheme.surfaceVariant) {
listOf(
copy(alpha = 0.3f),
copy(alpha = 0.5f),
copy(alpha = 0.7f),
copy(alpha = 0.5f),
copy(alpha = 0.3f),
)
}
): Modifier = if (isShimmerVisible) {
composed {
val shimmerColors = with(MaterialTheme.colorScheme.surfaceVariant) {
listOf(
copy(alpha = 0.3f),
copy(alpha = 0.5f),
copy(alpha = 0.7f),
copy(alpha = 0.5f),
copy(alpha = 0.3f),
)
}

val transition = rememberInfiniteTransition(label = "shimmer loading animation transition")
val transition = rememberInfiniteTransition(label = "shimmer loading animation transition")

val translateAnimation = transition.animateFloat(
initialValue = 0f,
targetValue = (durationMillis + widthOfShadowBrush).toFloat(),
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = durationMillis,
easing = LinearEasing,
val translateAnimation = transition.animateFloat(
initialValue = 0f,
targetValue = (durationMillis + widthOfShadowBrush).toFloat(),
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = durationMillis,
easing = LinearEasing,
),
repeatMode = RepeatMode.Restart,
),
repeatMode = RepeatMode.Restart,
),
label = "Shimmer loading animation",
)
label = "Shimmer loading animation",
)

background(
brush = Brush.linearGradient(
colors = shimmerColors,
start = Offset(x = translateAnimation.value - widthOfShadowBrush, y = 0.0f),
end = Offset(x = translateAnimation.value, y = angleOfAxisY),
),
)
background(
brush = Brush.linearGradient(
colors = shimmerColors,
start = Offset(x = translateAnimation.value - widthOfShadowBrush, y = 0.0f),
end = Offset(x = translateAnimation.value, y = angleOfAxisY),
),
)
}
} else {
this
}

@Composable
Expand Down
27 changes: 16 additions & 11 deletions core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi/BaseStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import com.stslex.core.core.AppDispatcher
import com.stslex.core.core.Logger
import com.stslex.core.core.coroutine.AppCoroutineScope
import com.stslex.core.core.coroutine.AppCoroutineScopeImpl
import com.stslex.core.core.coroutineExceptionHandler
import com.stslex.core.ui.mvi.Store.Action
import com.stslex.core.ui.mvi.Store.Event
Expand All @@ -29,6 +31,11 @@ abstract class BaseStore<S : State, E : Event, A : Action, N : Navigation>(
protected val lastAction: A?
get() = _lastAction

protected val scope: AppCoroutineScope = AppCoroutineScopeImpl(
scope = screenModelScope,
appDispatcher = appDispatcher
)

/**
* Sends an action to the store. Checks if the action is not the same as the last action.
* If the action is not the same as the last action, the last action is updated.
Expand Down Expand Up @@ -104,11 +111,10 @@ abstract class BaseStore<S : State, E : Event, A : Action, N : Navigation>(
onError: suspend (Throwable) -> Unit = {},
onSuccess: suspend CoroutineScope.(T) -> Unit = {},
action: suspend CoroutineScope.() -> T,
): Job = screenModelScope.launch(
context = exceptionHandler(onError) + appDispatcher.default,
block = {
onSuccess(action())
}
): Job = scope.launch(
onError = onError,
onSuccess = onSuccess,
action = action
)

/**
Expand All @@ -120,13 +126,12 @@ abstract class BaseStore<S : State, E : Event, A : Action, N : Navigation>(
* @see Job
* @see AppDispatcher
* */
protected fun <T> Flow<T>.launchFlow(
protected fun <T> Flow<T>.launch(
onError: suspend (cause: Throwable) -> Unit = {},
each: suspend (T) -> Unit
): Job = screenModelScope.launch(
context = exceptionHandler(onError) + appDispatcher.default,
block = {
collect(each)
}
): Job = scope.launch(
flow = this,
onError = onError,
each = each,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.stslex.core.ui.pager

import com.stslex.core.ui.base.AppError

sealed interface PagerLoadEvents {

data class Error(val error: AppError) : PagerLoadEvents
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.stslex.core.ui.pager

import com.stslex.core.ui.base.AppError

sealed interface PagerLoadState {

data object Data : PagerLoadState

data object Initial : PagerLoadState

data object Loading : PagerLoadState

data object Refresh : PagerLoadState

data object Retry : PagerLoadState

data class Error(val error: AppError) : PagerLoadState

data object Empty : PagerLoadState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.stslex.core.ui.pager

import com.stslex.core.core.paging.PagingCoreItem
import com.stslex.core.ui.base.paging.PagingItem

fun interface PagingMapper<T : PagingCoreItem, R : PagingItem> {

suspend operator fun invoke(item: T): R
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.stslex.core.ui.pager

import com.stslex.core.ui.base.paging.PagingItem
import com.stslex.core.ui.base.paging.PagingState
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow

interface StorePager<out Item : PagingItem> {

val state: StateFlow<PagingState<Item>>

val loadState: StateFlow<PagerLoadState>

val loadEvents: SharedFlow<PagerLoadEvents>

fun initialLoad()

fun load()

fun refresh()

fun retry()
}
Loading

0 comments on commit 91c3551

Please sign in to comment.