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

Add close screen button #304

Merged
merged 2 commits into from
Jul 21, 2024
Merged
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
5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ kotlin {
dependencies {
implementation(libs.bundles.instrumentedtest)
implementation(libs.compose.ui.test)
implementation(libs.sqldelight.android)
implementation(libs.multiplatform.settings)
implementation(libs.multiplatform.settings.test)
debugImplementation(libs.compose.ui.test.manifest)
}
}
Expand Down Expand Up @@ -173,7 +176,7 @@ android {
setProperty("archivesBaseName", "app-dev")
}

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner = "br.alexandregpereira.hunter.app.KoinTestRunner"
}

signingConfigs {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package br.alexandregpereira.hunter.app

import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
import br.alexandregpereira.hunter.data.Database
import br.alexandregpereira.hunter.localization.Language
import com.russhwolf.settings.MapSettings
import com.russhwolf.settings.Settings
import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.squareup.sqldelight.db.SqlDriver
import org.koin.core.context.startKoin
import org.koin.dsl.module

internal class KoinTestRunner : AndroidJUnitRunner() {

override fun newApplication(
cl: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(cl, KoinTestApplication::class.qualifiedName, context)
}
}

internal class KoinTestApplication : Application() {

override fun onCreate() {
super.onCreate()
startKoin {
initAndroidModules(this@KoinTestApplication)
allowOverride(true)
module {
factory<SqlDriver> {
AndroidSqliteDriver(
schema = Database.Schema,
context = get<Application>(),
name = null
)
}
single<Settings> { MapSettings() }
factory<Language> { Language.ENGLISH }
}.also {
modules(it)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.performTouchInput
import androidx.test.espresso.Espresso
import br.alexandregpereira.hunter.app.HunterApp
import org.junit.Rule
import org.junit.Test
Expand All @@ -36,8 +35,7 @@ class FolderListTest {
composeTestRule.onNodeWithText("Add to Folder").performClick()
composeTestRule.onNodeWithText("Folder name").performTextInput("Folder Test")
composeTestRule.onNodeWithText("Save").performClick()
Espresso.closeSoftKeyboard()
Espresso.pressBack()
composeTestRule.onNodeWithContentDescription("Close").performClick()
composeTestRule.waitUntil(timeoutMillis = 5000) {
composeTestRule.onNodeWithText("Folders").isDisplayed()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import org.koin.android.ext.koin.androidContext
import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
import org.koin.dsl.module

Expand All @@ -34,14 +35,18 @@ class HunterApplication : Application() {

private fun initKoin() {
startKoin {
androidContext(this@HunterApplication)
modules(
module {
factory { Firebase.analytics }
factory { Firebase.crashlytics }
}
)
initKoinModules()
initAndroidModules(this@HunterApplication)
}
}
}

internal fun KoinApplication.initAndroidModules(app: Application) {
androidContext(app)
modules(
module {
factory { Firebase.analytics }
factory { Firebase.crashlytics }
}
)
initKoinModules()
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module

internal fun KoinApplication.initKoinModules() {
allowOverride(false)
modules(domainModules)
modules(dataModules)
modules(
Expand Down
2 changes: 1 addition & 1 deletion app/src/jvmMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fun main() = application {
onCloseRequest = ::exitApplication,
title = "Monster Compendium",
state = rememberWindowState(
size = DpSize(520.dp, 920.dp)
size = DpSize(600.dp, 800.dp)
),
onKeyEvent = onKeyEvent@ { keyEvent ->
val keyPressedHandled = keyEvent.asKeyPressedHandled() ?: return@onKeyEvent false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ interface MutableAppLocalization : AppLocalization {
fun setLanguage(language: String)
}

internal class AppLocalizationImpl : MutableAppLocalization, AppReactiveLocalization {
internal class AppLocalizationImpl(
private val defaultLanguage: Language,
) : MutableAppLocalization, AppReactiveLocalization {

private var language: Language = getDefaultLanguage()
private var language: Language = defaultLanguage
private val _languageFlow: MutableSharedFlow<Language> = MutableSharedFlow(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
Expand All @@ -34,15 +36,9 @@ internal class AppLocalizationImpl : MutableAppLocalization, AppReactiveLocaliza
}

override fun setLanguage(language: String) {
this.language = Language.entries.firstOrNull { it.code == language } ?: getDefaultLanguage()
this.language = Language.entries.firstOrNull { it.code == language } ?: defaultLanguage
_languageFlow.tryEmit(this.language)
}

private fun getDefaultLanguage(): Language {
return Language.entries.firstOrNull {
it.code == getDeviceLangCode()
} ?: Language.ENGLISH
}
}

internal expect fun getDeviceLangCode(): String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ package br.alexandregpereira.hunter.localization.di
import br.alexandregpereira.hunter.localization.AppLocalization
import br.alexandregpereira.hunter.localization.AppLocalizationImpl
import br.alexandregpereira.hunter.localization.AppReactiveLocalization
import br.alexandregpereira.hunter.localization.Language
import br.alexandregpereira.hunter.localization.MutableAppLocalization
import br.alexandregpereira.hunter.localization.getDeviceLangCode
import org.koin.dsl.module

val localizationModule = module {
single { AppLocalizationImpl() }
factory<Language> { getDefaultLanguage() }
single { AppLocalizationImpl(get()) }
single<MutableAppLocalization> { get<AppLocalizationImpl>() }
single<AppLocalization> { get<AppLocalizationImpl>() }
single<AppReactiveLocalization> { get<AppLocalizationImpl>() }
}

private fun getDefaultLanguage(): Language {
return Language.entries.firstOrNull {
it.code == getDeviceLangCode()
} ?: Language.ENGLISH
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ import br.alexandregpereira.hunter.data.monster.local.dao.MonsterDao
import br.alexandregpereira.hunter.data.monster.lore.local.dao.MonsterLoreDao
import br.alexandregpereira.hunter.data.source.local.dao.AlternativeSourceDao
import br.alexandregpereira.hunter.data.spell.local.dao.SpellDao
import com.squareup.sqldelight.db.SqlDriver
import kotlinx.coroutines.CoroutineDispatcher
import org.koin.dsl.module

val databaseModule = module {
factory<SqlDriver> {
createSqlDriver()
}
single {
Database(createSqlDriver())
Database(get())
}
factory<AlternativeSourceDao> {
AlternativeSourceDaoImpl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import org.koin.dsl.module

val settingsDataModule = module {
factory { AlternativeSourceUrlBuilder(get()) }
single { get<Settings.Factory>().create("preferences") }
single<Settings> { get<Settings.Factory>().create("preferences") }
factory<SettingsRepository> { DefaultSettingsRepository(get()) }
getAdditionalSettingsModule()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package br.alexandregpereira.hunter.domain.sync

import br.alexandregpereira.hunter.domain.settings.SettingsRepository
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.single

fun interface IsFirstTime {
suspend operator fun invoke(): Boolean
}

fun interface ResetFirstTime {
suspend operator fun invoke()
}

private const val IsFirstTimeKey = "isFirstTimeKey"


internal fun IsFirstTime(
repository: SettingsRepository,
): IsFirstTime = IsFirstTime {
repository.getInt(key = IsFirstTimeKey).map { it ?: 1 }.map { it == 1 }.single()
}

internal fun ResetFirstTime(
repository: SettingsRepository,
): ResetFirstTime = ResetFirstTime {
repository.saveInt(key = IsFirstTimeKey, value = 0).single()
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package br.alexandregpereira.hunter.domain.sync
import br.alexandregpereira.hunter.domain.monster.lore.SyncMonstersLoreUseCase
import br.alexandregpereira.hunter.domain.settings.GetContentVersionUseCase
import br.alexandregpereira.hunter.domain.settings.GetLanguageUseCase
import br.alexandregpereira.hunter.domain.settings.IsLanguageSupported
import br.alexandregpereira.hunter.domain.settings.SaveContentVersionUseCase
import br.alexandregpereira.hunter.domain.settings.SaveLanguageUseCase
import br.alexandregpereira.hunter.domain.spell.SyncSpellsUseCase
Expand All @@ -46,6 +45,8 @@ class SyncUseCase internal constructor(
private val saveLanguageUseCase: SaveLanguageUseCase,
private val getContentVersionUseCase: GetContentVersionUseCase,
private val saveContentVersionUseCase: SaveContentVersionUseCase,
private val isFirstTime: IsFirstTime,
private val resetFirstTime: ResetFirstTime,
) {

private val contentVersion = 3
Expand All @@ -67,6 +68,7 @@ class SyncUseCase internal constructor(
runCatching { saveContentVersionUseCase(lastContentVersionRollback).single() }
}
}
resetFirstTime()
emit(SyncStatus.SYNCED)
} else {
emit(SyncStatus.IDLE)
Expand All @@ -77,7 +79,8 @@ class SyncUseCase internal constructor(
private fun isToSync(): Flow<Triple<Boolean, String, Int>> {
return isLangSyncScenario()
.zip(isContentVersionSyncScenario()) { (isLangSyncScenario, lastLanguageRollback), (isContentVersionSyncScenario, lastContentVersionRollback) ->
Triple((isLangSyncScenario || isContentVersionSyncScenario), lastLanguageRollback, lastContentVersionRollback)
val isToSync = isFirstTime() || isLangSyncScenario || isContentVersionSyncScenario
Triple(isToSync, lastLanguageRollback, lastContentVersionRollback)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@

package br.alexandregpereira.hunter.domain.sync.di

import br.alexandregpereira.hunter.domain.sync.IsFirstTime
import br.alexandregpereira.hunter.domain.sync.ResetFirstTime
import br.alexandregpereira.hunter.domain.sync.SyncUseCase
import org.koin.dsl.module

val syncDomainModule = module {
factory { SyncUseCase(get(), get(), get(), get(), get(), get(), get()) }
factory { IsFirstTime(get()) }
factory { ResetFirstTime(get()) }
factory { SyncUseCase(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
package br.alexandregpereira.hunter.folder.detail.ui

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolder
import br.alexandregpereira.hunter.folder.detail.FolderDetailState
import br.alexandregpereira.hunter.ui.compendium.CompendiumItemState.Item
Expand All @@ -30,9 +28,7 @@ import br.alexandregpereira.hunter.ui.compendium.monster.MonsterCardState
import br.alexandregpereira.hunter.ui.compendium.monster.MonsterCompendium
import br.alexandregpereira.hunter.ui.compendium.monster.MonsterImageState
import br.alexandregpereira.hunter.ui.compendium.monster.MonsterTypeState
import br.alexandregpereira.hunter.ui.compose.BackHandler
import br.alexandregpereira.hunter.ui.compose.SwipeVerticalToDismiss
import br.alexandregpereira.hunter.ui.compose.Window
import br.alexandregpereira.hunter.ui.compose.AppFullScreen

@Composable
internal fun FolderDetailScreen(
Expand All @@ -41,29 +37,23 @@ internal fun FolderDetailScreen(
onItemCLick: (index: String) -> Unit = {},
onItemLongCLick: (index: String) -> Unit = {},
onClose: () -> Unit = {}
) {
BackHandler(enabled = state.isOpen, onBack = onClose)

) = AppFullScreen(isOpen = state.isOpen, contentPadding, onClose = onClose) {
val monsters = remember(state.monsters) { state.monsters.asState() }
SwipeVerticalToDismiss(visible = state.isOpen, onClose = onClose) {
Window(Modifier.fillMaxSize()) {
val items = listOf(
Title(
value = state.folderName,
isHeader = true
),
) + monsters.map {
Item(value = it)
}
MonsterCompendium(
items = items,
animateItems = true,
contentPadding = contentPadding,
onItemCLick = onItemCLick,
onItemLongCLick = onItemLongCLick
)
}
val items = listOf(
Title(
value = state.folderName,
isHeader = true
),
) + monsters.map {
Item(value = it)
}
MonsterCompendium(
items = items,
animateItems = true,
contentPadding = contentPadding,
onItemCLick = onItemCLick,
onItemLongCLick = onItemLongCLick
)
}

private fun List<MonsterPreviewFolder>.asState(): List<MonsterCardState> = map {
Expand Down
Loading
Loading