Skip to content

Commit

Permalink
Add image content scale to appearance settings (#329)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandregpereira authored Aug 20, 2024
1 parent 3304763 commit 9a54412
Show file tree
Hide file tree
Showing 39 changed files with 274 additions and 69 deletions.
1 change: 0 additions & 1 deletion domain/monster-folder/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ plugins {

multiplatform {
commonMain {
implementation(project(":domain:monster:core"))
implementation(libs.koin.core)
implementation(libs.kotlin.coroutines.core)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,11 @@ data class MonsterPreviewFolder(
val imageUrl: String = "",
val backgroundColorLight: String,
val backgroundColorDark: String,
val imageContentScale: MonsterPreviewFolderImageContentScale,
val isHorizontalImage: Boolean = false,
)

enum class MonsterPreviewFolderImageContentScale {
Fit,
Crop
}
1 change: 1 addition & 0 deletions domain/monster-folder/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ multiplatform {
commonMain {
implementation(project(":domain:monster:data"))
implementation(project(":domain:monster-folder:core"))
implementation(project(":domain:settings:core"))
implementation(libs.kotlin.coroutines.core)
implementation(libs.kotlin.datetime)
implementation(libs.koin.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,37 @@ import br.alexandregpereira.hunter.domain.folder.FolderMonsterPreviewRepository
import br.alexandregpereira.hunter.domain.folder.MonsterFolderRepository
import br.alexandregpereira.hunter.domain.folder.model.MonsterFolder
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolder
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolderImageContentScale
import br.alexandregpereira.hunter.domain.settings.AppSettingsImageContentScale
import br.alexandregpereira.hunter.domain.settings.GetAppearanceSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.single

internal class DefaultFolderRepository(
private val monsterFolderLocalDataSource: MonsterFolderLocalDataSource
private val monsterFolderLocalDataSource: MonsterFolderLocalDataSource,
private val getAppearanceSettings: GetAppearanceSettings,
) : MonsterFolderRepository, FolderMonsterPreviewRepository {

override fun addMonsters(folderName: String, indexes: List<String>): Flow<Unit> {
return monsterFolderLocalDataSource.addMonsters(folderName, monsterIndexes = indexes)
}

override fun getMonsterFolders(): Flow<List<MonsterFolder>> {
return monsterFolderLocalDataSource.getMonsterFolders().map { it.asDomain() }
return monsterFolderLocalDataSource.getMonsterFolders().map {
it.asDomain(getImageContentScale())
}
}

override fun getMonstersFromFolder(folderName: String): Flow<MonsterFolder?> {
return monsterFolderLocalDataSource.getMonstersFromFolder(folderName).map { it?.asDomain() }
return monsterFolderLocalDataSource.getMonstersFromFolder(folderName).map {
it?.asDomain(getImageContentScale())
}
}

override fun getMonstersFromFolders(foldersName: List<String>): Flow<List<MonsterPreviewFolder>> {
return monsterFolderLocalDataSource.getMonstersFromFolders(foldersName)
.map { it.asDomainMonsterPreviewFolderEntity() }
.map { it.asDomainMonsterPreviewFolderEntity(getImageContentScale()) }
}

override fun removeMonsters(folderName: String, indexes: List<String>): Flow<Unit> {
Expand All @@ -53,10 +62,19 @@ internal class DefaultFolderRepository(
indexes: List<String>
): Flow<List<MonsterPreviewFolder>> {
return monsterFolderLocalDataSource.getFolderMonsterPreviewsByIds(indexes)
.map { it.asDomainMonsterPreviewFolderEntity() }
.map { it.asDomainMonsterPreviewFolderEntity(getImageContentScale()) }
}

override fun removeMonsterFolders(folderNames: List<String>): Flow<Unit> {
return monsterFolderLocalDataSource.removeMonsterFolders(folderNames)
}

private suspend fun getImageContentScale(): MonsterPreviewFolderImageContentScale {
return getAppearanceSettings().single().let { settings ->
when (settings.imageContentScale) {
AppSettingsImageContentScale.Fit -> MonsterPreviewFolderImageContentScale.Fit
AppSettingsImageContentScale.Crop -> MonsterPreviewFolderImageContentScale.Crop
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,27 @@ import br.alexandregpereira.hunter.data.monster.folder.local.entity.MonsterFolde
import br.alexandregpereira.hunter.data.monster.local.entity.MonsterEntity
import br.alexandregpereira.hunter.domain.folder.model.MonsterFolder
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolder
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolderImageContentScale
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolderType

internal fun List<MonsterFolderCompleteEntity>.asDomain(): List<MonsterFolder> {
return this.map { it.asDomain() }
internal fun List<MonsterFolderCompleteEntity>.asDomain(
monsterImageContentScale: MonsterPreviewFolderImageContentScale
): List<MonsterFolder> {
return this.map { it.asDomain(monsterImageContentScale) }
}

internal fun MonsterFolderCompleteEntity.asDomain(): MonsterFolder {
internal fun MonsterFolderCompleteEntity.asDomain(
monsterImageContentScale: MonsterPreviewFolderImageContentScale
): MonsterFolder {
return MonsterFolder(
name = monsterFolderEntity.folderName,
monsters = monsters.asDomainMonsterPreviewFolderEntity()
monsters = monsters.asDomainMonsterPreviewFolderEntity(monsterImageContentScale)
)
}

internal fun List<MonsterEntity>.asDomainMonsterPreviewFolderEntity(): List<MonsterPreviewFolder> {
internal fun List<MonsterEntity>.asDomainMonsterPreviewFolderEntity(
monsterImageContentScale: MonsterPreviewFolderImageContentScale
): List<MonsterPreviewFolder> {
return map {
it.run {
MonsterPreviewFolder(
Expand All @@ -45,6 +52,7 @@ internal fun List<MonsterEntity>.asDomainMonsterPreviewFolderEntity(): List<Mons
backgroundColorLight = backgroundColorLight,
backgroundColorDark = backgroundColorDark,
isHorizontalImage = isHorizontalImage,
imageContentScale = monsterImageContentScale,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import org.koin.dsl.module
val monsterFolderDataModule = module {
factory<MonsterFolderLocalDataSource> { DefaultMonsterFolderLocalDataSource(get(), get()) }
factory {
createMonsterFolderRepository() ?: DefaultFolderRepository(get())
createMonsterFolderRepository() ?: DefaultFolderRepository(get(), get())
}
factory {
createFolderMonsterPreviewRepository() ?: DefaultFolderRepository(get())
createFolderMonsterPreviewRepository() ?: DefaultFolderRepository(get(), get())
}
}.apply { includes(getAdditionalModule()) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,14 @@ fun Float.getChallengeRatingFormatted(): String {
data class MonsterImageData(
val url: String = "",
val backgroundColor: Color = Color(),
val isHorizontal: Boolean = false
val isHorizontal: Boolean = false,
val contentScale: MonsterImageContentScale = MonsterImageContentScale.Fit,
)

enum class MonsterImageContentScale {
Fit, Crop
}

data class Color(
val light: String = "",
val dark: String = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,41 +22,52 @@ import br.alexandregpereira.hunter.data.monster.local.mapper.toDomainMonsterEnti
import br.alexandregpereira.hunter.data.monster.local.mapper.toEntity
import br.alexandregpereira.hunter.data.monster.local.mapper.toEntityStatus
import br.alexandregpereira.hunter.domain.model.Monster
import br.alexandregpereira.hunter.domain.model.MonsterImageContentScale
import br.alexandregpereira.hunter.domain.model.MonsterStatus
import br.alexandregpereira.hunter.domain.repository.MonsterLocalRepository
import br.alexandregpereira.hunter.domain.settings.AppSettingsImageContentScale
import br.alexandregpereira.hunter.domain.settings.GetAppearanceSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.single

internal class DefaultMonsterLocalRepository(
private val localDataSource: MonsterLocalDataSource
private val localDataSource: MonsterLocalDataSource,
private val getAppearanceSettings: GetAppearanceSettings,
) : MonsterLocalRepository {

override fun saveMonsters(monsters: List<Monster>, isSync: Boolean): Flow<Unit> {
return localDataSource.saveMonsters(monsters.toEntity(), isSync)
}

override fun getMonsterPreviews(): Flow<List<Monster>> {
return localDataSource.getMonsterPreviews().map { it.toDomainMonsterEntity() }
return localDataSource.getMonsterPreviews().map {
it.toDomainMonsterEntity(getImageContentScale())
}
}

override fun getMonsterPreviewsEdited(): Flow<List<Monster>> {
return localDataSource.getMonsterPreviewsEdited().map { it.toDomainMonsterEntity() }
return localDataSource.getMonsterPreviewsEdited().map {
it.toDomainMonsterEntity(getImageContentScale())
}
}

override fun getMonsters(): Flow<List<Monster>> {
return localDataSource.getMonsters().map { it.toDomain() }
return localDataSource.getMonsters().map { it.toDomain(getImageContentScale()) }
}

override fun getMonsters(indexes: List<String>): Flow<List<Monster>> {
return localDataSource.getMonsters(indexes).map { it.toDomain() }
return localDataSource.getMonsters(indexes).map { it.toDomain(getImageContentScale()) }
}

override fun getMonster(index: String): Flow<Monster> {
return localDataSource.getMonster(index).map { it.toDomain() }
return localDataSource.getMonster(index).map { it.toDomain(getImageContentScale()) }
}

override fun getMonstersByQuery(query: String): Flow<List<Monster>> {
return localDataSource.getMonstersByQuery(query).map { it.toDomainMonsterEntity() }
return localDataSource.getMonstersByQuery(query).map {
it.toDomainMonsterEntity(getImageContentScale())
}
}

override fun deleteMonster(index: String): Flow<Unit> {
Expand All @@ -65,6 +76,15 @@ internal class DefaultMonsterLocalRepository(

override fun getMonstersByStatus(status: Set<MonsterStatus>): Flow<List<Monster>> {
return localDataSource.getMonstersByStatus(status.map { it.toEntityStatus() }.toSet())
.map { it.toDomain() }
.map { it.toDomain(getImageContentScale()) }
}

private suspend fun getImageContentScale(): MonsterImageContentScale {
return getAppearanceSettings().single().imageContentScale.let { imageContentScale ->
when (imageContentScale) {
AppSettingsImageContentScale.Fit -> MonsterImageContentScale.Fit
AppSettingsImageContentScale.Crop -> MonsterImageContentScale.Crop
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ val monsterDataModule = module {
single { MonsterCacheDataSource() }
factory<MonsterRepository> { MonsterRepositoryImpl(get(), get()) }
factory {
createMonsterLocalRepository() ?: DefaultMonsterLocalRepository(get())
createMonsterLocalRepository() ?: DefaultMonsterLocalRepository(get(), get())
}
factory<MonsterRemoteRepository> { MonsterRemoteRepositoryImpl(get(), get()) }
factory<MonsterCacheRepository> { MonsterCacheRepositoryImpl(get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,34 @@ import br.alexandregpereira.hunter.data.monster.spell.local.mapper.toEntity
import br.alexandregpereira.hunter.domain.model.ChallengeRating
import br.alexandregpereira.hunter.domain.model.Color
import br.alexandregpereira.hunter.domain.model.Monster
import br.alexandregpereira.hunter.domain.model.MonsterImageContentScale
import br.alexandregpereira.hunter.domain.model.MonsterImageData
import br.alexandregpereira.hunter.domain.model.MonsterStatus
import br.alexandregpereira.hunter.domain.model.MonsterType
import br.alexandregpereira.hunter.domain.model.Speed
import br.alexandregpereira.hunter.domain.model.Stats

fun List<MonsterCompleteEntity>.toDomain(): List<Monster> {
fun List<MonsterCompleteEntity>.toDomain(
monsterImageContentScale: MonsterImageContentScale,
): List<Monster> {
return this.map {
it.toDomain()
it.toDomain(monsterImageContentScale)
}
}

fun List<MonsterEntity>.toDomainMonsterEntity(): List<Monster> {
fun List<MonsterEntity>.toDomainMonsterEntity(
monsterImageContentScale: MonsterImageContentScale,
): List<Monster> {
return this.map {
it.toDomain()
it.toDomain(monsterImageContentScale)
}
}

internal fun MonsterCompleteEntity.toDomain(): Monster {
internal fun MonsterCompleteEntity.toDomain(
monsterImageContentScale: MonsterImageContentScale,
): Monster {
val monster = this.monster
return monster.toDomain().copy(
return monster.toDomain(monsterImageContentScale).copy(
speed = this.speed?.toDomain() ?: Speed(hover = false, values = emptyList()),
abilityScores = this.abilityScores.toDomain(),
savingThrows = this.savingThrows.toDomain(),
Expand Down Expand Up @@ -114,7 +121,9 @@ internal fun List<Monster>.toEntity(): List<MonsterCompleteEntity> {
return this.map { it.toEntity() }
}

private fun MonsterEntity.toDomain(): Monster {
private fun MonsterEntity.toDomain(
monsterImageContentScale: MonsterImageContentScale,
): Monster {
val monster = this
return Monster(
index = monster.index,
Expand All @@ -127,7 +136,8 @@ private fun MonsterEntity.toDomain(): Monster {
light = monster.backgroundColorLight,
dark = monster.backgroundColorDark
),
isHorizontal = monster.isHorizontalImage
isHorizontal = monster.isHorizontalImage,
contentScale = monsterImageContentScale
),
subtype = monster.subtype,
group = monster.group,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ internal fun SaveAppearanceSettings(
"forceLightImageBackground" to appearance.forceLightImageBackground.toString(),
"defaultLightBackground" to appearance.defaultLightBackground,
"defaultDarkBackground" to appearance.defaultDarkBackground,
"imageContentScale" to when (appearance.imageContentScale) {
AppSettingsImageContentScale.Fit -> "Fit"
AppSettingsImageContentScale.Crop -> "Crop"
}
)
)
}
Expand All @@ -32,18 +36,32 @@ internal fun GetAppearanceSettings(
.single()?.toBoolean() ?: false
val defaultLightBackground = settings.getString("defaultLightBackground").single() ?: ""
val defaultDarkBackground = settings.getString("defaultDarkBackground").single() ?: ""
val imageContentScale = settings.getString("imageContentScale").single()?.let {
when (it) {
"Fit" -> AppSettingsImageContentScale.Fit
"Crop" -> AppSettingsImageContentScale.Crop
else -> null
}
} ?: AppSettingsImageContentScale.Fit
AppearanceSettings(
forceLightImageBackground = forceLightImageBackground,
defaultLightBackground = defaultLightBackground,
defaultDarkBackground = defaultDarkBackground,
imageContentScale = imageContentScale,
).let {
emit(it)
}
}
}

data class AppearanceSettings(
val forceLightImageBackground: Boolean = false,
val defaultLightBackground: String = "",
val defaultDarkBackground: String = "",
val forceLightImageBackground: Boolean,
val defaultLightBackground: String,
val defaultDarkBackground: String,
val imageContentScale: AppSettingsImageContentScale,
)

enum class AppSettingsImageContentScale {
Fit,
Crop,
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolder
import br.alexandregpereira.hunter.domain.folder.model.MonsterPreviewFolderImageContentScale
import br.alexandregpereira.hunter.folder.detail.di.FolderDetailStateRecoveryQualifier
import br.alexandregpereira.hunter.folder.detail.ui.FolderDetailScreen
import br.alexandregpereira.hunter.ui.compendium.monster.ColorState
import br.alexandregpereira.hunter.ui.compendium.monster.MonsterCardState
import br.alexandregpereira.hunter.ui.compendium.monster.MonsterImageState
import br.alexandregpereira.hunter.ui.compendium.monster.MonsterTypeState
import br.alexandregpereira.hunter.ui.compose.AppImageContentScale
import br.alexandregpereira.hunter.ui.compose.StateRecoveryLaunchedEffect
import org.koin.compose.koinInject
import org.koin.core.qualifier.named
Expand Down Expand Up @@ -71,7 +73,11 @@ private fun List<MonsterPreviewFolder>.asState(): List<MonsterCardState> = map {
),
challengeRating = challengeRating,
isHorizontal = isHorizontalImage,
contentDescription = name
contentDescription = name,
contentScale = when (imageContentScale) {
MonsterPreviewFolderImageContentScale.Fit -> AppImageContentScale.Fit
MonsterPreviewFolderImageContentScale.Crop -> AppImageContentScale.Crop
}
)
)
}
Expand Down
Loading

0 comments on commit 9a54412

Please sign in to comment.