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

Lesson & course progress on BE #66

Merged
merged 14 commits into from
Dec 7, 2024
8 changes: 2 additions & 6 deletions composeApp/src/commonMain/kotlin/data/AnalyticsRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package data

import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensureNotNull
import domain.SessionManager
import ivy.data.source.AnalyticsDataSource
import ivy.model.analytics.AnalyticsEventDto
Expand All @@ -19,12 +18,9 @@ class AnalyticsRepository(
events: Set<AnalyticsEventDto>,
): Either<String, Unit> = either {
withContext(dispatchers.io) {
val session = sessionManager.getSession()
ensureNotNull(session) {
"No session found. Please login"
}
val session = sessionManager.getSession().bind()
dataSource.logEvents(
sessionToken = session,
session = session,
request = LogAnalyticsEventsRequest(
events = events,
)
Expand Down
29 changes: 0 additions & 29 deletions composeApp/src/commonMain/kotlin/data/LessonRepository.kt

This file was deleted.

6 changes: 1 addition & 5 deletions composeApp/src/commonMain/kotlin/data/UserRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package data

import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensureNotNull
import domain.SessionManager
import ivy.data.source.UserDataSource
import kotlinx.coroutines.withContext
Expand All @@ -17,10 +16,7 @@ class UserRepository(
) {
suspend fun deleteUserData(): Either<String, Unit> = withContext(dispatches.io) {
either {
val session = sessionManager.getSession()
ensureNotNull(session) {
"No user session"
}
val session = sessionManager.getSession().bind()
logger.info("Initiating delete user data request...")
userDataSource.deleteUserData(session).bind()
}
Expand Down
16 changes: 6 additions & 10 deletions composeApp/src/commonMain/kotlin/data/di/DataModule.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package data.di

import AppConfiguration
import data.*
import data.fake.FakeLessonRepository
import data.lesson.LessonRepository
import data.lesson.LessonRepositoryImpl
import data.lesson.mapper.LessonMapper
import data.storage.LocalStorage
import data.storage.localStorage
import di.bindWithFake
Expand All @@ -12,21 +14,15 @@ import ivy.di.autowire.autoWire

object DataModule : Di.Module {
override fun init() = Di.appScope {
register { TopicsRepository(Di.get(), Di.get()) }
register { CourseRepository(Di.get(), Di.get()) }
autoWire(::TopicsRepository)
autoWire(::CourseRepository)
autoWire(::LessonRepositoryImpl)
autoWire(::FakeLessonRepository)
register<LessonRepository> {
if (Di.get<AppConfiguration>().fakesEnabled) {
Di.get<FakeLessonRepository>()
} else {
Di.get<LessonRepositoryImpl>()
}
}
bindWithFake<LessonRepository, LessonRepositoryImpl, FakeLessonRepository>()
register<LocalStorage> { localStorage() }
autoWire(::UserRepository)
autoWire(::AnalyticsRepository)
autoWire(::LessonMapper)
autoWire(::SoundRepository)
}
}
35 changes: 27 additions & 8 deletions composeApp/src/commonMain/kotlin/data/fake/FakeLessonRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package data.fake

import arrow.core.Either
import arrow.core.right
import data.LessonRepository
import data.lesson.LessonRepository
import domain.model.LessonProgress
import domain.model.LessonWithProgress
import ivy.content.lesson.programmingfundamentals.programmingMathInDisguise
import ivy.model.CourseId
import ivy.model.ImageUrl
Expand All @@ -13,13 +15,30 @@ class FakeLessonRepository : LessonRepository {
override suspend fun fetchLesson(
course: CourseId,
lesson: LessonId
): Either<String, Lesson> {
return Lesson(
id = LessonId("fake"),
name = "Programming: Math in disguise",
tagline = "",
image = ImageUrl(""),
content = programmingMathInDisguise(),
): Either<String, LessonWithProgress> {
return LessonWithProgress(
lesson = Lesson(
id = LessonId("fake"),
name = "Programming: Math in disguise",
tagline = "",
image = ImageUrl(""),
content = programmingMathInDisguise(),
),
progress = LessonProgress(
selectedAnswers = mapOf(),
openAnswersInput = mapOf(),
chosen = mapOf(),
answered = setOf(),
completed = setOf()
)
).right()
}

override suspend fun saveLessonProgress(
course: CourseId,
lesson: LessonId,
progress: LessonProgress
): Either<String, Unit> {
return Either.Right(Unit)
}
}
75 changes: 75 additions & 0 deletions composeApp/src/commonMain/kotlin/data/lesson/LessonRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package data.lesson

import arrow.core.Either
import arrow.core.raise.either
import data.lesson.mapper.LessonMapper
import domain.SessionManager
import domain.model.LessonProgress
import domain.model.LessonWithProgress
import ivy.data.source.LessonDataSource
import ivy.model.CourseId
import ivy.model.LessonId
import ivy.model.lesson.LessonProgressDto
import kotlinx.coroutines.withContext
import util.DispatchersProvider

class LessonRepositoryImpl(
private val dispatchers: DispatchersProvider,
private val datasource: LessonDataSource,
private val mapper: LessonMapper,
private val sessionManager: SessionManager,
) : LessonRepository {

override suspend fun fetchLesson(
course: CourseId,
lesson: LessonId
): Either<String, LessonWithProgress> = either {
withContext(dispatchers.io) {
val session = sessionManager.getSession().bind()
datasource.fetchLesson(
session = session,
course = course,
lesson = lesson
).map {
with(mapper) {
it.toDomain()
}
}.bind()
}
}

override suspend fun saveLessonProgress(
course: CourseId,
lesson: LessonId,
progress: LessonProgress
): Either<String, Unit> = either {
withContext(dispatchers.io) {
val session = sessionManager.getSession().bind()
datasource.saveProgress(
session = session,
course = course,
lesson = lesson,
progress = LessonProgressDto(
selectedAnswers = progress.selectedAnswers,
openAnswersInput = progress.openAnswersInput,
chosen = progress.chosen,
answered = progress.answered,
completed = progress.completed,
)
)
}
}
}

interface LessonRepository {
suspend fun fetchLesson(
course: CourseId,
lesson: LessonId
): Either<String, LessonWithProgress>

suspend fun saveLessonProgress(
course: CourseId,
lesson: LessonId,
progress: LessonProgress,
): Either<String, Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package data.lesson.mapper

import domain.model.LessonProgress
import domain.model.LessonWithProgress
import ivy.model.lesson.LessonResponse

class LessonMapper {
fun LessonResponse.toDomain(): LessonWithProgress {
return LessonWithProgress(
lesson = lesson,
progress = LessonProgress(
selectedAnswers = progress?.selectedAnswers ?: emptyMap(),
openAnswersInput = progress?.openAnswersInput ?: emptyMap(),
chosen = progress?.chosen ?: emptyMap(),
answered = progress?.answered ?: emptySet(),
completed = progress?.completed ?: emptySet()
)
)
}
}
13 changes: 12 additions & 1 deletion composeApp/src/commonMain/kotlin/domain/SessionManager.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package domain

import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensureNotNull
import data.storage.LocalStorage
import ivy.model.auth.SessionToken

Expand All @@ -13,7 +16,15 @@ class SessionManager(
sessionToken = token
}

suspend fun getSession(): SessionToken? {
suspend fun getSession(): Either<String, SessionToken> = either {
val session = getSessionOrNull()
ensureNotNull(session) {
"No session found. Please login"
}
session
}

suspend fun getSessionOrNull(): SessionToken? {
return sessionToken ?: localStorage.getString(SESSION_TOKEN_KEY)
?.let(::SessionToken)
?.also {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain.model

import ivy.model.AnswerId
import ivy.model.ChoiceOptionId
import ivy.model.Lesson
import ivy.model.LessonItemId

data class LessonWithProgress(
val lesson: Lesson,
val progress: LessonProgress,
)

data class LessonProgress(
val selectedAnswers: Map<LessonItemId, Set<AnswerId>>,
val openAnswersInput: Map<LessonItemId, String>,
val chosen: Map<LessonItemId, ChoiceOptionId>,
val answered: Set<LessonItemId>,
val completed: Set<LessonItemId>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@ import domain.SessionManager
import domain.analytics.Analytics
import domain.analytics.Source
import ivy.model.auth.SessionToken
import navigation.Navigation
import ui.screen.home.HomeScreen

class GoogleAuthRedirect(
private val platform: Platform,
private val sessionManager: SessionManager,
private val analytics: Analytics,
private val navigation: Navigation,
) : Redirect {
override val name = "GoogleAuth"

Expand All @@ -25,7 +22,6 @@ class GoogleAuthRedirect(
source = Source.Intro,
event = "continue_with_google"
)
navigation.replaceWith(HomeScreen())
return true
}
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class LoggedOutUserRedirect(
override val name = "Logged-out user"

override suspend fun handle(): Boolean {
if (sessionManager.getSession() == null) {
if (sessionManager.getSessionOrNull() == null) {
navigation.replaceWith(IntroScreen())
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package ui.screen.lesson
import androidx.compose.runtime.Composable
import arrow.core.Option
import arrow.core.raise.option
import domain.analytics.Analytics
import ivy.di.Di
import ivy.di.Di.register
import ivy.di.autowire.autoWire
Expand Down Expand Up @@ -57,7 +56,7 @@ class LessonScreen(
autoWire(::LessonViewStateMapper)
autoWire(::OnBackClickEventHandler)
autoWire(::OnContinueClickEventHandler)
autoWire(::QuestionViewEventHandler)
autoWire(::QuestionEventHandler)
autoWire(::OnSoundClickEventHandler)
autoWire(::OnFinishClickEventHandler)
autoWire(::OnChoiceClickEventHandler)
Expand All @@ -72,12 +71,13 @@ class LessonScreen(
eventHandlers = setOf(
Di.get<OnBackClickEventHandler>(),
Di.get<OnContinueClickEventHandler>(),
Di.get<QuestionViewEventHandler>(),
Di.get<QuestionEventHandler>(),
Di.get<OnSoundClickEventHandler>(),
Di.get<OnFinishClickEventHandler>(),
Di.get<OnChoiceClickEventHandler>(),
),
analytics = Di.get<Analytics>()
analytics = Di.get(),
logger = Di.get()
)
}
}
Expand Down
Loading
Loading