Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev #33

Merged
merged 3 commits into from
Apr 28, 2024
Merged

dev #33

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading