Add settings store/repo etc. to save user settings.
This commit is contained in:
parent
992c79a100
commit
2058a6a789
12 changed files with 238 additions and 19 deletions
|
|
@ -2,6 +2,7 @@ package com.pixelized.desktop.lwa
|
|||
|
||||
import com.pixelized.desktop.lwa.business.CharacterSheetUseCase
|
||||
import com.pixelized.desktop.lwa.business.RollUseCase
|
||||
import com.pixelized.desktop.lwa.business.SettingsUseCase
|
||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
||||
import com.pixelized.desktop.lwa.business.SkillValueComputationUseCase
|
||||
import com.pixelized.desktop.lwa.parser.arithmetic.ArithmeticParser
|
||||
|
|
@ -11,6 +12,9 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore
|
|||
import com.pixelized.desktop.lwa.repository.characterSheet.SkillDescriptionFactory
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsFactory
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsStore
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetFactory
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetViewModel
|
||||
import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditFactory
|
||||
|
|
@ -21,6 +25,7 @@ import com.pixelized.desktop.lwa.screen.network.NetworkFactory
|
|||
import com.pixelized.desktop.lwa.screen.network.NetworkViewModel
|
||||
import com.pixelized.desktop.lwa.screen.roll.RollViewModel
|
||||
import com.pixelized.desktop.lwa.screen.rollhistory.RollHistoryViewModel
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.core.module.dsl.viewModelOf
|
||||
|
|
@ -28,19 +33,32 @@ import org.koin.dsl.module
|
|||
|
||||
val moduleDependencies
|
||||
get() = listOf(
|
||||
toolsDependencies,
|
||||
parserDependencies,
|
||||
factoryDependencies,
|
||||
useCaseDependencies,
|
||||
storeDependencies,
|
||||
repositoryDependencies,
|
||||
viewModelDependencies,
|
||||
useCaseDependencies,
|
||||
)
|
||||
|
||||
val toolsDependencies
|
||||
get() = module {
|
||||
factory { Json { explicitNulls = false } }
|
||||
}
|
||||
|
||||
val storeDependencies
|
||||
get() = module {
|
||||
singleOf(::CharacterSheetStore)
|
||||
singleOf(::SettingsStore)
|
||||
}
|
||||
|
||||
val repositoryDependencies
|
||||
get() = module {
|
||||
singleOf(::CharacterSheetStore)
|
||||
singleOf(::NetworkRepository)
|
||||
singleOf(::CharacterSheetRepository)
|
||||
singleOf(::RollHistoryRepository)
|
||||
singleOf(::SettingsRepository)
|
||||
}
|
||||
|
||||
val factoryDependencies
|
||||
|
|
@ -51,6 +69,7 @@ val factoryDependencies
|
|||
factoryOf(::NetworkFactory)
|
||||
factoryOf(::SkillFieldFactory)
|
||||
factoryOf(::SkillDescriptionFactory)
|
||||
factoryOf(::SettingsFactory)
|
||||
}
|
||||
|
||||
val viewModelDependencies
|
||||
|
|
@ -74,4 +93,5 @@ val useCaseDependencies
|
|||
factoryOf(::RollUseCase)
|
||||
factoryOf(::SkillValueComputationUseCase)
|
||||
factoryOf(::CharacterSheetUseCase)
|
||||
factoryOf(::SettingsUseCase)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.pixelized.desktop.lwa.business
|
||||
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||
|
||||
class SettingsUseCase {
|
||||
|
||||
fun defaultSettings(): Settings = Settings(
|
||||
playerName = "",
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.pixelized.desktop.lwa.repository.characterSheet
|
|||
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -12,7 +13,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||
class CharacterSheetRepository(
|
||||
private val store: CharacterSheetStore,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
private val sheets = store.characterSheetFlow()
|
||||
.stateIn(
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import java.io.File
|
|||
|
||||
class CharacterSheetStore(
|
||||
private val factory: CharacterSheetJsonFactory,
|
||||
private val jsonFormatter: Json,
|
||||
) {
|
||||
private val characterDirectory = File(characterStorePath()).also { it.mkdirs() }
|
||||
private val jsonFormatter: Json = Json { explicitNulls = false }
|
||||
private val flow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||
|
||||
init {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package com.pixelized.desktop.lwa.repository.network
|
|||
//import io.ktor.server.netty.NettyApplicationEngine
|
||||
import com.pixelized.desktop.lwa.repository.network.helper.client
|
||||
import com.pixelized.desktop.lwa.repository.network.helper.connectWebSocket
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||
import com.pixelized.desktop.lwa.utils.extention.decodeFromFrame
|
||||
import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
|
||||
import com.pixelized.server.lwa.SERVER_PORT
|
||||
|
|
@ -26,7 +27,9 @@ import kotlinx.serialization.json.Json
|
|||
|
||||
typealias Client = HttpClient
|
||||
|
||||
class NetworkRepository {
|
||||
class NetworkRepository(
|
||||
private val settingsRepository: SettingsRepository,
|
||||
) {
|
||||
companion object {
|
||||
const val DEFAULT_PORT = SERVER_PORT
|
||||
const val DEFAULT_HOST = "pixelized.freeboxos.fr"
|
||||
|
|
@ -40,15 +43,9 @@ class NetworkRepository {
|
|||
private val incomingMessageBuffer = MutableSharedFlow<Message>()
|
||||
val data: SharedFlow<Message> get() = incomingMessageBuffer
|
||||
|
||||
private val _player = MutableStateFlow("")
|
||||
val player: StateFlow<String> get() = _player
|
||||
|
||||
private val _status = MutableStateFlow(Status.DISCONNECTED)
|
||||
val status: StateFlow<Status> get() = _status
|
||||
|
||||
fun onPlayerNameChange(player: String) {
|
||||
_player.value = player
|
||||
}
|
||||
|
||||
fun connect(
|
||||
host: String,
|
||||
|
|
@ -101,11 +98,12 @@ class NetworkRepository {
|
|||
}
|
||||
|
||||
suspend fun share(
|
||||
playerName: String = settingsRepository.settings().playerName,
|
||||
content: MessageContent,
|
||||
) {
|
||||
if (status.value == Status.CONNECTED) {
|
||||
val message = Message(
|
||||
from = player.value,
|
||||
from = playerName,
|
||||
value = content,
|
||||
)
|
||||
// emit the message into the outgoing buffer
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package com.pixelized.desktop.lwa.repository.settings
|
||||
|
||||
import com.pixelized.desktop.lwa.business.SettingsUseCase
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJsonV1
|
||||
|
||||
|
||||
class SettingsFactory(
|
||||
private val useCase: SettingsUseCase,
|
||||
) {
|
||||
|
||||
fun convertToJson(
|
||||
settings: Settings,
|
||||
): SettingsJson {
|
||||
return SettingsJsonV1(
|
||||
playerName = settings.playerName,
|
||||
)
|
||||
}
|
||||
|
||||
fun convertFromJson(
|
||||
json: SettingsJson,
|
||||
): Settings {
|
||||
return when (json) {
|
||||
is SettingsJsonV1 -> convertFromJsonV1(json)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertFromJsonV1(
|
||||
json: SettingsJsonV1,
|
||||
): Settings {
|
||||
return with(useCase.defaultSettings()) {
|
||||
Settings(
|
||||
playerName = json.playerName ?: playerName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.pixelized.desktop.lwa.repository.settings
|
||||
|
||||
import com.pixelized.desktop.lwa.business.SettingsUseCase
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class SettingsRepository(
|
||||
private val store: SettingsStore,
|
||||
private val useCase: SettingsUseCase,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
private val settings = store.settingsFlow()
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = useCase.defaultSettings()
|
||||
)
|
||||
|
||||
fun settingsFlow(): StateFlow<Settings> = settings
|
||||
|
||||
fun settings(): Settings = settings.value
|
||||
|
||||
fun update(settings: Settings) {
|
||||
store.save(settings = settings)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
store.save(settings = useCase.defaultSettings())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.pixelized.desktop.lwa.repository.settings
|
||||
|
||||
import com.pixelized.desktop.lwa.business.SettingsUseCase
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson
|
||||
import com.pixelized.desktop.lwa.repository.storePath
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
|
||||
class SettingsStore(
|
||||
private val factory: SettingsFactory,
|
||||
private val useCase: SettingsUseCase,
|
||||
private val jsonFormatter: Json,
|
||||
) {
|
||||
private val settingsDirectory = File(storePath()).also { it.mkdirs() }
|
||||
private val flow = MutableStateFlow(value = useCase.defaultSettings())
|
||||
|
||||
fun settingsFlow(): StateFlow<Settings> = flow
|
||||
|
||||
init {
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
scope.launch {
|
||||
flow.value = load()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(
|
||||
SettingsStoreException::class,
|
||||
FileWriteException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun save(settings: Settings) {
|
||||
val json = try {
|
||||
factory.convertToJson(settings = settings).let(jsonFormatter::encodeToString)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
try {
|
||||
val file = settingsFile()
|
||||
file.writeText(
|
||||
text = json,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(
|
||||
root = exception
|
||||
)
|
||||
}
|
||||
flow.value = settings
|
||||
}
|
||||
|
||||
private fun load(): Settings {
|
||||
return settingsFile().let { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(
|
||||
root = exception
|
||||
)
|
||||
}
|
||||
if (json.isBlank()) {
|
||||
return useCase.defaultSettings()
|
||||
}
|
||||
try {
|
||||
factory.convertFromJson(
|
||||
json = jsonFormatter.decodeFromString<SettingsJson>(json)
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun settingsFile(): File {
|
||||
return File("${storePath()}settings.json")
|
||||
}
|
||||
|
||||
sealed class SettingsStoreException(root: Exception) : Exception(root)
|
||||
class JsonConversionException(root: Exception) : SettingsStoreException(root)
|
||||
class FileWriteException(root: Exception) : SettingsStoreException(root)
|
||||
class FileReadException(root: Exception) : SettingsStoreException(root)
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.pixelized.desktop.lwa.repository.settings.model
|
||||
|
||||
data class Settings(
|
||||
val playerName: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.pixelized.desktop.lwa.repository.settings.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface SettingsJson
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.pixelized.desktop.lwa.repository.settings.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SettingsJsonV1(
|
||||
val playerName: String?,
|
||||
) : SettingsJson
|
||||
|
|
@ -12,13 +12,16 @@ import androidx.lifecycle.viewModelScope
|
|||
import com.pixelized.desktop.lwa.composable.blur.BlurContentController
|
||||
import com.pixelized.desktop.lwa.composable.error.snack.ErrorSnackUio
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||
import com.pixelized.desktop.lwa.utils.extention.collectAsState
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NetworkViewModel(
|
||||
private val repository: NetworkRepository,
|
||||
private val factory: NetworkFactory
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val networkRepository: NetworkRepository,
|
||||
private val factory: NetworkFactory,
|
||||
) : ViewModel() {
|
||||
private val host = mutableStateOf(NetworkRepository.DEFAULT_HOST)
|
||||
private val port = mutableStateOf(NetworkRepository.DEFAULT_PORT)
|
||||
|
|
@ -38,8 +41,8 @@ class NetworkViewModel(
|
|||
@Composable
|
||||
@Stable
|
||||
get() {
|
||||
val player = repository.player.collectAsState()
|
||||
val status = repository.status.collectAsState()
|
||||
val player = settingsRepository.settingsFlow().collectAsState { it.playerName }
|
||||
val status = networkRepository.status.collectAsState()
|
||||
return remember {
|
||||
derivedStateOf {
|
||||
factory.convertToUio(
|
||||
|
|
@ -53,7 +56,11 @@ class NetworkViewModel(
|
|||
}
|
||||
|
||||
fun onPlayerNameChange(player: String) {
|
||||
repository.onPlayerNameChange(player = player)
|
||||
settingsRepository.update(
|
||||
settings = settingsRepository.settings().copy(
|
||||
playerName = player,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun onPortChange(port: String) {
|
||||
|
|
@ -68,7 +75,7 @@ class NetworkViewModel(
|
|||
controller.show()
|
||||
_isLoading.value = true
|
||||
|
||||
repository.connect(
|
||||
networkRepository.connect(
|
||||
host = host.value,
|
||||
port = port.value,
|
||||
onConnect = {
|
||||
|
|
@ -86,6 +93,6 @@ class NetworkViewModel(
|
|||
}
|
||||
|
||||
fun disconnect() {
|
||||
repository.disconnect()
|
||||
networkRepository.disconnect()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue