Skip to content

Commit

Permalink
Merge branch 'dev' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxBQb committed May 27, 2022
2 parents 9a2328b + c6f386e commit 4a9b503
Show file tree
Hide file tree
Showing 22 changed files with 259 additions and 83 deletions.
20 changes: 17 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,18 @@ android {
it.srcDirs += "build/generated/ksp/${variant.name}/kotlin"
}
}
testOptions {
unitTests.all {
useJUnitPlatform()
}
}
}

dependencies {
// Dependency Injection
def koin_annotations_version = '1.0.0-beta-2'
implementation 'io.insert-koin:koin-android:3.2.0-beta-1'
def koin_annotations_version = '1.0.0-beta-1'
def koin_version = "3.2.0"
implementation "io.insert-koin:koin-android:$koin_version"
implementation "io.insert-koin:koin-annotations:$koin_annotations_version"
ksp "io.insert-koin:koin-ksp-compiler:$koin_annotations_version"

Expand Down Expand Up @@ -127,4 +133,12 @@ dependencies {

// Desugaring (Time-related features support for API 21+)
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}

// Tests
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
// Koin
testImplementation "io.insert-koin:koin-test:$koin_version"
testImplementation "io.insert-koin:koin-test-junit4:$koin_version"
testImplementation "io.insert-koin:koin-test-junit5:$koin_version"
}
3 changes: 3 additions & 0 deletions app/src/main/java/lab/maxb/dark/di/FlexibleDIModule.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package lab.maxb.dark.di

import lab.maxb.dark.presentation.repository.network.dark.DarkService
import lab.maxb.dark.presentation.repository.network.dark.DarkServiceImpl
import lab.maxb.dark.presentation.repository.room.LocalDatabase
import org.koin.android.ext.koin.androidApplication
import org.koin.dsl.module

internal val flexibleDIModule = module {
single { LocalDatabase.build(androidApplication()) }
single<DarkService> { DarkServiceImpl(get()) }
}
2 changes: 1 addition & 1 deletion app/src/main/java/lab/maxb/dark/domain/model/Profile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ enum class Role {
USER,
}

fun Role.isUser() = when(this) {
val Role.isUser get() = when(this) {
Role.USER, Role.PREMIUM_USER -> true
else -> false
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@ class ProfileRepositoryImpl(
private val profileDAO = db.profileDao()
private val _login = MutableStateFlow(userSettings.login)

@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
@OptIn(ExperimentalCoroutinesApi::class)
override val profile = _login.flatMapLatest { login ->
profileDAO.getByLogin(login).distinctUntilChanged().mapLatest { fullProfile ->
fullProfile?.toProfile()?.also {
it.user?.id?.let { id ->
assert(checkToken(id))
}
}
fullProfile?.toProfile()
}
}

Expand All @@ -53,12 +49,5 @@ class ProfileRepositoryImpl(
_login.value = login
}

private suspend fun checkToken(id: String) = try {
usersRepository.getUser(id).firstOrNull()!!
true
} catch (e: Throwable) {
false
}

override suspend fun save(profile: Profile) = profileDAO.save(ProfileDTO(profile))
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ class RecognitionTasksRepositoryImpl(
}
}

override suspend fun solveRecognitionTask(id: String, answer: String)
= try {
mDarkService.solveTask(id, answer)
} catch (e: Throwable) {
e.printStackTrace()
false
}

override suspend fun deleteRecognitionTask(task: RecognitionTask) {
mRecognitionTaskDao.deleteRecognitionTask(
task as RecognitionTaskDTO
Expand Down Expand Up @@ -175,7 +183,6 @@ class RecognitionTasksRepositoryImpl(
}
}

@OptIn(ExperimentalCoroutinesApi::class)
override suspend fun getRecognitionTask(id: String, forceUpdate: Boolean)
= taskResource.query(id, forceUpdate)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package lab.maxb.dark.presentation.repository.implementation

import kotlinx.coroutines.flow.Flow
import lab.maxb.dark.domain.model.User
import lab.maxb.dark.presentation.repository.interfaces.UsersRepository
import lab.maxb.dark.presentation.repository.network.dark.DarkService
import lab.maxb.dark.presentation.repository.room.LocalDatabase
import lab.maxb.dark.presentation.repository.room.dao.UserDAO
import lab.maxb.dark.presentation.repository.room.model.UserDTO
import lab.maxb.dark.presentation.repository.utils.StaticResource
import lab.maxb.dark.presentation.repository.utils.Resource
import org.koin.core.annotation.Single

@Single
Expand All @@ -15,13 +16,13 @@ class UsersRepositoryImpl(
private val darkService: DarkService
) : UsersRepository {
private val mUserDao: UserDAO = db.userDao()
private val userResource = StaticResource<String, User>().apply {
private val userResource = Resource<String, User>().apply {
fetchLocal = { mUserDao.getUser(it) }
fetchRemote = { darkService.getUser(it) }
localStore = { mUserDao.addUser(UserDTO(it)) }
localStore = { mUserDao.save(UserDTO(it)) }
clearLocalStore = { mUserDao.deleteUser(it) }
}

override suspend fun getUser(id: String)
= userResource.query(id)
override suspend fun getUser(id: String, fresh: Boolean): Flow<User?>
= userResource.query(id, fresh)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ interface RecognitionTasksRepository {
suspend fun getRecognitionTask(id: String, forceUpdate: Boolean = false): Flow<RecognitionTask?>
suspend fun addRecognitionTask(task: RecognitionTask)
suspend fun markRecognitionTask(task: RecognitionTask)
suspend fun solveRecognitionTask(id: String, answer: String): Boolean
suspend fun deleteRecognitionTask(task: RecognitionTask)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import kotlinx.coroutines.flow.Flow
import lab.maxb.dark.domain.model.User

interface UsersRepository {
suspend fun getUser(id: String): Flow<User?>
suspend fun getUser(id: String, fresh: Boolean = false): Flow<User?>
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,20 @@
package lab.maxb.dark.presentation.repository.network.dark

import com.google.gson.GsonBuilder
import lab.maxb.dark.BuildConfig
import lab.maxb.dark.presentation.repository.network.dark.routes.Auth
import lab.maxb.dark.presentation.repository.network.dark.routes.RecognitionTask
import lab.maxb.dark.presentation.repository.network.dark.routes.User
import lab.maxb.dark.presentation.repository.network.logger
import okhttp3.OkHttpClient
import org.koin.core.annotation.Single
import org.koin.java.KoinJavaComponent.get
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import lab.maxb.dark.domain.model.User
import lab.maxb.dark.presentation.repository.network.dark.model.*
import okhttp3.MultipartBody
import okhttp3.ResponseBody


interface DarkService :
RecognitionTask,
User,
Auth

@Single
class DarkServiceImpl: DarkService by buildDarkService()

private fun buildDarkService()
= Retrofit.Builder()
.baseUrl(BuildConfig.DARK_API_URL)
.addConverterFactory(converter)
.client(okhttpClient)
.build()
.create(DarkService::class.java)

private val okhttpClient get() = OkHttpClient.Builder()
.addInterceptor(logger)
.addInterceptor(get<AuthInterceptor>(AuthInterceptor::class.java))
.build()

private val converter get() = GsonBuilder().apply {
setLenient()
}.create().run {
GsonConverterFactory.create(this)
}
interface DarkService {
suspend fun getAllTasks(page: Int, size: Int): List<RecognitionTaskListViewDTO>?
suspend fun getTask(id: String): RecognitionTaskFullViewDTO?
suspend fun addTask(task: RecognitionTaskCreationDTO): String?
suspend fun markTask(id: String, isAllowed: Boolean): Boolean
suspend fun solveTask(id: String, answer: String): Boolean
suspend fun addImage(id: String, filePart: MultipartBody.Part): String?
suspend fun downloadImage(path: String): ResponseBody?
suspend fun getUser(id: String): User?
suspend fun login(request: AuthRequest): AuthResponse
suspend fun signup(request: AuthRequest): AuthResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package lab.maxb.dark.presentation.repository.network.dark

import lab.maxb.dark.presentation.repository.network.dark.routes.Auth
import lab.maxb.dark.presentation.repository.network.dark.routes.RecognitionTask
import lab.maxb.dark.presentation.repository.network.dark.routes.User

interface DarkServiceAPI :
RecognitionTask,
User,
Auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package lab.maxb.dark.presentation.repository.network.dark

import com.google.gson.GsonBuilder
import lab.maxb.dark.BuildConfig
import lab.maxb.dark.presentation.repository.network.dark.model.AuthRequest
import lab.maxb.dark.presentation.repository.network.dark.model.RecognitionTaskCreationDTO
import lab.maxb.dark.presentation.repository.network.logger
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import org.koin.core.annotation.Single
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.EOFException

@Single
class DarkServiceImpl(
private val authInterceptor: AuthInterceptor,
) : DarkService {
private val api = buildDarkService()

override suspend fun getAllTasks(page: Int, size: Int) = catchAll {
api.getAllTasks(page, size)
}

override suspend fun getTask(id: String) = catchAll {
api.getTask(id)
}

override suspend fun addTask(task: RecognitionTaskCreationDTO) = catchAll {
api.addTask(task)
}

override suspend fun markTask(id: String, isAllowed: Boolean) = catchAll {
api.markTask(id, isAllowed)
}

override suspend fun solveTask(id: String, answer: String) = catchAll {
api.solveTask(id, answer)
}

override suspend fun addImage(id: String, filePart: MultipartBody.Part) = catchAll {
api.addImage(id, filePart)
}

override suspend fun downloadImage(path: String) = catchAll {
api.downloadImage(path)
}

override suspend fun getUser(id: String) = catchAll {
api.getUser(id)
}

override suspend fun login(request: AuthRequest) = catchAll {
api.login(request)
}

override suspend fun signup(request: AuthRequest) = catchAll {
api.signup(request)
}

private suspend inline fun<reified T> catchAll(
crossinline block: suspend () -> T
): T = try {
block()
} catch (e: EOFException) {
null as T
} catch (e: Throwable) {
e.printStackTrace()
throw e
}

// Initialization
private fun buildDarkService()
= Retrofit.Builder()
.baseUrl(BuildConfig.DARK_API_URL)
.addConverterFactory(converter)
.client(okhttpClient)
.build()
.create(DarkServiceAPI::class.java)

private val okhttpClient get() = OkHttpClient.Builder()
.addInterceptor(logger)
.addInterceptor(authInterceptor)
.build()

private val converter get() = GsonBuilder().apply {
setLenient()
}.create().run {
GsonConverterFactory.create(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ interface RecognitionTask {
@Path("isAllowed") isAllowed: Boolean
): Boolean

@GET("$path/solve/{id}")
suspend fun solveTask(
@Path("id") id: String,
@Query("answer") answer: String
): Boolean

@Multipart
@POST("$path/{id}/image")
suspend fun addImage(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package lab.maxb.dark.presentation.repository.room.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy.REPLACE
import androidx.room.Query
import androidx.room.*
import androidx.room.OnConflictStrategy.*
import kotlinx.coroutines.flow.Flow
import lab.maxb.dark.presentation.repository.room.model.UserDTO

@Dao
interface UserDAO {
@Insert(onConflict = REPLACE)
suspend fun addUser(user: UserDTO)
@Insert(onConflict = IGNORE)
suspend fun addUser(user: UserDTO): Long

@Update
suspend fun updateUser(user: UserDTO)

@Transaction
suspend fun save(user: UserDTO) {
if (addUser(user) == -1L)
updateUser(user)
}

@Query("DELETE FROM user WHERE id = :id")
suspend fun deleteUser(id: String)
Expand Down
Loading

0 comments on commit 4a9b503

Please sign in to comment.