diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/DomainResponse.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/DomainResponse.kt index 60565f8..6501171 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/DomainResponse.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/DomainResponse.kt @@ -12,6 +12,9 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import org.koin.core.component.KoinComponent import org.koin.core.component.get +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.jvm.JvmInline sealed interface DomainResponse { @@ -24,24 +27,6 @@ sealed interface DomainResponse { data object Loading : DomainResponse } -inline fun DomainResponseSource( - crossinline fetch: suspend (ArgosLanguage) -> T, - crossinline transform: (T) -> R, -): DomainResponseSource { - return DomainResponseSource { - try { - val result = fetch(it) - if (result.message == "ok") { - DomainResponse.Success(transform(result)) - } else { - DomainResponse.Error(result.errors!!.general[0]) - } - } catch (e: Exception) { - DomainResponse.Error(e.message ?: e.stackTraceToString()) - } - } -} - inline fun combine( flow1: Flow>, flow2: Flow>, @@ -61,22 +46,30 @@ inline fun combine( } } -class DomainResponseSource( - private inline val compute: suspend (ArgosLanguage) -> DomainResponse +class DomainResponseSource( + private inline val fetch: suspend (ArgosLanguage) -> T, + private inline val transform: (T) -> R, ): KoinComponent { private val settings: ArgosSettings = get() - private val state = MutableStateFlow>(DomainResponse.Loading) + private val state = MutableStateFlow>(DomainResponse.Loading) - fun asFlow(): Flow> { + fun asFlow(): Flow> { return combine(state, settings.observeLanguage()) { state, language -> - return@combine if (state is DomainResponse.Loading) { - compute(language).also { + state.takeUnlessOr(predicate = { it is DomainResponse.Loading }) { + try { + val result = fetch(language) + if (result.message == "ok") { + DomainResponse.Success(transform(result)) + } else { + DomainResponse.Error(result.errors!!.general[0]) + } + } catch (e: Exception) { + DomainResponse.Error(e.message ?: e.stackTraceToString()) + }.also { this.state.value = it } - } else { - state } }.flowOn(Dispatchers.IO) } @@ -85,5 +78,17 @@ class DomainResponseSource( state.value = DomainResponse.Loading } + @OptIn(ExperimentalContracts::class) + private inline fun T.takeUnlessOr( + predicate: (T) -> Boolean, + unless: () -> T + ): T { + contract { + callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) + callsInPlace(unless, InvocationKind.EXACTLY_ONCE) + } + return if (!predicate(this)) this else unless() + } + suspend operator fun invoke() = asFlow().first() } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/courses/CoursesRepository.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/courses/CoursesRepository.kt index dc975f1..e91dea5 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/courses/CoursesRepository.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/courses/CoursesRepository.kt @@ -12,7 +12,7 @@ class CoursesRepository( private val argosApi: ArgosApi ) { - fun getCourseSyllabus(courseId: String): DomainResponseSource { + fun getCourseSyllabus(courseId: String): DomainResponseSource<*, DomainCourseSyllabus> { return DomainResponseSource( fetch = { argosApi.getCourseSyllabus(courseId) @@ -45,7 +45,7 @@ class CoursesRepository( ) } - fun getCourseScores(courseId: String): DomainResponseSource { + fun getCourseScores(courseId: String): DomainResponseSource<*, DomainCourseScores> { return DomainResponseSource( fetch = { argosApi.getCourseScores(courseId) @@ -64,7 +64,7 @@ class CoursesRepository( ) } - fun getCourseClassmates(courseId: String): DomainResponseSource> { + fun getCourseClassmates(courseId: String): DomainResponseSource<*, List> { return DomainResponseSource( fetch = { argosApi.getCourseClassmates(courseId) @@ -81,7 +81,7 @@ class CoursesRepository( ) } - fun getCourseGroups(courseId: String): DomainResponseSource> { + fun getCourseGroups(courseId: String): DomainResponseSource<*, List> { return DomainResponseSource( fetch = { argosApi.getCourseGroups(courseId) @@ -103,7 +103,7 @@ class CoursesRepository( ) } - fun getCourseChosenGroup(courseId: String): DomainResponseSource { + fun getCourseChosenGroup(courseId: String): DomainResponseSource<*, DomainCourseChosenGroup> { return DomainResponseSource( fetch = { argosApi.getCourseChosenGroup(courseId) @@ -123,7 +123,7 @@ class CoursesRepository( fun getCourseGroupSchedule( courseId: String, groupId: String - ): DomainResponseSource> { + ): DomainResponseSource<*, List> { return DomainResponseSource( fetch = { argosApi.getCourseGroupSchedule(courseId, groupId) diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/messages/MessagesRepository.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/messages/MessagesRepository.kt index 817a048..5dd74b8 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/messages/MessagesRepository.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/messages/MessagesRepository.kt @@ -57,7 +57,7 @@ class MessagesRepository( ) } - fun getMessage(id: String, semId: String): DomainResponseSource { + fun getMessage(id: String, semId: String): DomainResponseSource<*, DomainMessage> { return DomainResponseSource( fetch = { argosApi.getMessage(id, semId)