Change the character sheet save system to use separate files for sheets
This commit is contained in:
		
							parent
							
								
									de13bf2c74
								
							
						
					
					
						commit
						4606122264
					
				
					 7 changed files with 95 additions and 70 deletions
				
			
		| 
						 | 
					@ -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/"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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))
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								output.zip
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								output.zip
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue