Change the character sheet save system to use separate files for sheets

This commit is contained in:
Thomas Andres Gomez 2024-11-28 22:14:06 +01:00
parent de13bf2c74
commit 4606122264
7 changed files with 95 additions and 70 deletions

View file

@ -1,27 +1,37 @@
package com.pixelized.desktop.lwa.repository package com.pixelized.desktop.lwa.repository
fun operatingSystem(
name: String = System.getProperty("os.name")
): OperatingSystem {
return when {
name.contains(other = "win", ignoreCase = true) -> OperatingSystem.Windows
name.contains(other = "mac", ignoreCase = true) -> OperatingSystem.Macintosh
else -> error("Unsupported operating system: $name")
}
}
enum class OperatingSystem( enum class OperatingSystem(
val home: String, val home: String = System.getProperty("user.home"),
) { ) {
Windows(home = System.getProperty("user.home")), Windows,
Macintosh(home = System.getProperty("user.home")), Macintosh;
companion object {
val current: OperatingSystem = run {
val name = System.getProperty("os.name")
when {
name.contains(other = "win", ignoreCase = true) -> Windows
name.contains(other = "mac", ignoreCase = true) -> Macintosh
else -> error("Unsupported operating system: $name")
}
}
}
} }
fun storePath( fun storePath(
operatingSystem: OperatingSystem = operatingSystem() os: OperatingSystem = OperatingSystem.current,
): String { ): String {
return when (operatingSystem) { return when (os) {
OperatingSystem.Windows -> "${operatingSystem.home}\\AppData\\Roaming\\Pixelized\\" OperatingSystem.Windows -> "${os.home}\\AppData\\Roaming\\Pixelized\\"
OperatingSystem.Macintosh -> "${operatingSystem.home}/Library/Pixelized/" OperatingSystem.Macintosh -> "${os.home}/Library/Pixelized/"
}
}
fun characterStorePath(
os: OperatingSystem = OperatingSystem.current,
): String {
return when (os) {
OperatingSystem.Windows -> "${storePath(os = os)}characters\\"
OperatingSystem.Macintosh -> "${storePath(os = os)}characters/"
} }
} }

View file

@ -52,23 +52,11 @@ class CharacterSheetRepository(
} }
fun save(characterSheet: CharacterSheet) { fun save(characterSheet: CharacterSheet) {
val savedSheets = store.load().toMutableList() store.save(sheet = characterSheet)
val savedIndex = savedSheets.indexOfFirst { it.id == characterSheet.id }
if (savedIndex >= 0) {
// this sheet is already saved. update it
savedSheets[savedIndex] = characterSheet
} else {
// add the character sheet to the list.
savedSheets.add(characterSheet)
}
// save the list of characters sheet.
store.save(sheets = savedSheets)
} }
fun delete(id: String) { fun delete(id: String) {
val savedSheets = store.load().toMutableList() store.delete(id = id)
savedSheets.removeIf { it.id == id }
store.save(sheets = savedSheets)
} }
fun setDiminishedForCharacter(id: String, diminished: Int) { fun setDiminishedForCharacter(id: String, diminished: Int) {

View file

@ -2,7 +2,7 @@ package com.pixelized.desktop.lwa.repository.characterSheet
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJson import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheetJson
import com.pixelized.desktop.lwa.repository.storePath import com.pixelized.desktop.lwa.repository.characterStorePath
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
@ -12,8 +12,7 @@ import java.io.File
class CharacterSheetStore( class CharacterSheetStore(
private val factory: CharacterSheetJsonFactory, private val factory: CharacterSheetJsonFactory,
) { ) {
private val root = File(storePath()).also { it.mkdirs() } private val characterDirectory = File(characterStorePath()).also { it.mkdirs() }
private val files = File(root, NAME).also { it.createNewFile() }
private val flow = MutableStateFlow(value = load()) private val flow = MutableStateFlow(value = load())
fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow fun characterSheetFlow(): StateFlow<List<CharacterSheet>> = flow
@ -23,23 +22,49 @@ class CharacterSheetStore(
FileWriteException::class, FileWriteException::class,
JsonConversionException::class, JsonConversionException::class,
) )
fun save(sheets: List<CharacterSheet>) { fun save(sheet: CharacterSheet) {
// convert the character sheet into json format.
val json = try { val json = try {
sheets factory.convertToJson(sheet = sheet).let(Json::encodeToString)
.map(factory::convertToJson)
.let(Json::encodeToString)
} catch (exception: Exception) { } catch (exception: Exception) {
throw JsonConversionException(root = exception) throw JsonConversionException(root = exception)
} }
// write the character file.
try { try {
files.writeText( val file = characterSheetFile(id = sheet.id)
file.writeText(
text = json, text = json,
charset = Charsets.UTF_8, charset = Charsets.UTF_8,
) )
} catch (exception: Exception) { } catch (exception: Exception) {
throw FileWriteException(root = exception) throw FileWriteException(root = exception)
} }
flow.value = sheets // Update the dataflow.
flow.value = flow.value
.toMutableList()
.also { data ->
val index = data.indexOfFirst { it.id == sheet.id }
if (index >= 0) {
data[index] = sheet
} else {
data.add(sheet)
}
}
.sortedBy {
it.name
}
}
fun delete(id: String): Boolean {
val file = characterSheetFile(id = id)
flow.value = flow.value.toMutableList()
.also { data ->
data.removeIf { it.id == id }
}
.sortedBy {
it.name
}
return file.delete()
} }
@Throws( @Throws(
@ -48,25 +73,33 @@ class CharacterSheetStore(
JsonConversionException::class, JsonConversionException::class,
) )
fun load(): List<CharacterSheet> { fun load(): List<CharacterSheet> {
val json = try { return characterDirectory
files.readText(charset = Charsets.UTF_8) .listFiles()
} catch (exception: Exception) { ?.mapNotNull { file ->
throw FileReadException(root = exception) val json = try {
} file.readText(charset = Charsets.UTF_8)
return if (json.isBlank()) { } catch (exception: Exception) {
emptyList() throw FileReadException(root = exception)
} else { }
try { // Guard, if the json is blank no character have been save, ignore this file.
val sheets = Json.decodeFromString<List<CharacterSheetJson>>(json) if (json.isBlank()) {
sheets.map { factory.convertFromJson(it) } return@mapNotNull null
} catch (exception: Exception) { }
throw JsonConversionException(root = exception) try {
val sheet = Json.decodeFromString<CharacterSheetJson>(json)
factory.convertFromJson(sheet)
} catch (exception: Exception) {
throw JsonConversionException(root = exception)
}
} }
} ?.sortedBy {
it.name
}
?: emptyList()
} }
companion object { private fun characterSheetFile(id: String): File {
private const val NAME = "characters_sheet.json" return File("${characterStorePath()}${id}.json")
} }
sealed class CharacterSheetStoreException(root: Exception) : Exception(root) sealed class CharacterSheetStoreException(root: Exception) : Exception(root)

View file

@ -69,8 +69,8 @@ data class CharacterSheetEditPageUio(
val charisma: SimpleFieldUio, val charisma: SimpleFieldUio,
val movement: SimpleFieldUio, val movement: SimpleFieldUio,
val maxHp: SimpleFieldUio, val maxHp: SimpleFieldUio,
val currentHp: SimpleFieldUio,
val maxPp: SimpleFieldUio, val maxPp: SimpleFieldUio,
val currentHp: SimpleFieldUio,
val currentPp: SimpleFieldUio, val currentPp: SimpleFieldUio,
val damageBonus: SimpleFieldUio, val damageBonus: SimpleFieldUio,
val armor: SimpleFieldUio, val armor: SimpleFieldUio,

View file

@ -34,7 +34,7 @@ class SkillFieldFactory {
return SkillFieldUio( return SkillFieldUio(
id = id, id = id,
label = createWrapper( label = createWrapper(
label = mutableStateOf(labelValue), label = mutableStateOf(label),
value = labelValue, value = labelValue,
), ),
base = createWrapper( base = createWrapper(

View file

@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
import com.lordcodes.turtle.shellRun import com.lordcodes.turtle.shellRun
import com.pixelized.desktop.lwa.repository.OperatingSystem import com.pixelized.desktop.lwa.repository.OperatingSystem
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.operatingSystem
import com.pixelized.desktop.lwa.repository.storePath import com.pixelized.desktop.lwa.repository.storePath
import com.pixelized.desktop.lwa.utils.extention.collectAsState import com.pixelized.desktop.lwa.utils.extention.collectAsState
@ -28,17 +27,12 @@ class MainPageViewModel(
} }
} }
fun openSaveDirectory() { fun openSaveDirectory(
when (val os = operatingSystem()) { os: OperatingSystem = OperatingSystem.current
OperatingSystem.Windows -> shellRun( ) {
"explorer.exe", when (os) {
listOf(storePath(operatingSystem = os)) OperatingSystem.Windows -> shellRun("explorer.exe", listOf(storePath(os = os)))
) OperatingSystem.Macintosh -> shellRun("open", listOf(storePath(os = os)))
OperatingSystem.Macintosh -> shellRun(
"open",
listOf(storePath(operatingSystem = os))
)
} }
} }
} }

Binary file not shown.