feat: add Create New Character with confirmation (Closes #30) #73
@@ -77,6 +77,12 @@ object TestTags {
|
|||||||
const val SETTINGS_THEME_DARK = "settings_theme_dark"
|
const val SETTINGS_THEME_DARK = "settings_theme_dark"
|
||||||
const val SETTINGS_EXPORT_BUTTON = "settings_export_button"
|
const val SETTINGS_EXPORT_BUTTON = "settings_export_button"
|
||||||
const val SETTINGS_IMPORT_BUTTON = "settings_import_button"
|
const val SETTINGS_IMPORT_BUTTON = "settings_import_button"
|
||||||
|
const val SETTINGS_NEW_CHARACTER_BUTTON = "settings_new_character_button"
|
||||||
|
|
||||||
|
// --- New character confirmation dialog ---
|
||||||
|
const val NEW_CHARACTER_CONFIRM_DIALOG = "new_character_confirm_dialog"
|
||||||
|
const val NEW_CHARACTER_CONFIRM = "new_character_confirm"
|
||||||
|
const val NEW_CHARACTER_DISMISS = "new_character_dismiss"
|
||||||
|
|
||||||
// --- Export dialog ---
|
// --- Export dialog ---
|
||||||
const val EXPORT_DIALOG = "export_dialog"
|
const val EXPORT_DIALOG = "export_dialog"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.selection.selectable
|
|||||||
import androidx.compose.foundation.selection.selectableGroup
|
import androidx.compose.foundation.selection.selectableGroup
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.Download
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material.icons.filled.Upload
|
import androidx.compose.material.icons.filled.Upload
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@@ -19,6 +20,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import org.shahondin1624.lib.components.TestTags
|
import org.shahondin1624.lib.components.TestTags
|
||||||
import org.shahondin1624.lib.functions.DataLoader
|
import org.shahondin1624.lib.functions.DataLoader
|
||||||
import org.shahondin1624.model.charactermodel.ShadowrunCharacter
|
import org.shahondin1624.model.charactermodel.ShadowrunCharacter
|
||||||
|
import org.shahondin1624.model.createNewCharacter
|
||||||
import org.shahondin1624.theme.LocalThemePreference
|
import org.shahondin1624.theme.LocalThemePreference
|
||||||
import org.shahondin1624.theme.ThemePreference
|
import org.shahondin1624.theme.ThemePreference
|
||||||
|
|
||||||
@@ -33,6 +35,38 @@ fun SettingsPage(
|
|||||||
var themePreference by LocalThemePreference.current
|
var themePreference by LocalThemePreference.current
|
||||||
var showExportDialog by remember { mutableStateOf(false) }
|
var showExportDialog by remember { mutableStateOf(false) }
|
||||||
var showImportDialog by remember { mutableStateOf(false) }
|
var showImportDialog by remember { mutableStateOf(false) }
|
||||||
|
var showNewCharacterConfirm by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// New character confirmation dialog
|
||||||
|
if (showNewCharacterConfirm && onImportCharacter != null) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showNewCharacterConfirm = false },
|
||||||
|
modifier = Modifier.testTag(TestTags.NEW_CHARACTER_CONFIRM_DIALOG),
|
||||||
|
title = { Text("Create New Character") },
|
||||||
|
text = {
|
||||||
|
Text("This will replace your current character with a blank one. Any unsaved changes will be lost. Are you sure?")
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onImportCharacter(createNewCharacter())
|
||||||
|
showNewCharacterConfirm = false
|
||||||
|
},
|
||||||
|
modifier = Modifier.testTag(TestTags.NEW_CHARACTER_CONFIRM)
|
||||||
|
) {
|
||||||
|
Text("Create")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { showNewCharacterConfirm = false },
|
||||||
|
modifier = Modifier.testTag(TestTags.NEW_CHARACTER_DISMISS)
|
||||||
|
) {
|
||||||
|
Text("Cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Export dialog
|
// Export dialog
|
||||||
if (showExportDialog && character != null) {
|
if (showExportDialog && character != null) {
|
||||||
@@ -163,6 +197,28 @@ fun SettingsPage(
|
|||||||
Text("Import")
|
Text("Import")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { showNewCharacterConfirm = true },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(TestTags.SETTINGS_NEW_CHARACTER_BUTTON)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Add,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Text("New Character")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,42 @@ val EXAMPLE_ATTRIBUTES = Attributes(
|
|||||||
edge = 2
|
edge = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val DEFAULT_ATTRIBUTES = Attributes(
|
||||||
|
body = Attribute(AttributeType.Body, 1),
|
||||||
|
agility = Attribute(AttributeType.Agility, 1),
|
||||||
|
reaction = Attribute(AttributeType.Reaction, 1),
|
||||||
|
strength = Attribute(AttributeType.Strength, 1),
|
||||||
|
willpower = Attribute(AttributeType.Willpower, 1),
|
||||||
|
logic = Attribute(AttributeType.Logic, 1),
|
||||||
|
intuition = Attribute(AttributeType.Intuition, 1),
|
||||||
|
charisma = Attribute(AttributeType.Charisma, 1),
|
||||||
|
edge = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a fresh blank character with all attributes at 1, all talents at rating 0,
|
||||||
|
* and default character data. Used for the "New Character" action.
|
||||||
|
*/
|
||||||
|
fun createNewCharacter(): ShadowrunCharacter = ShadowrunCharacter(
|
||||||
|
attributes = DEFAULT_ATTRIBUTES,
|
||||||
|
talents = Talents(createAllProvidedTalents()),
|
||||||
|
characterData = CharacterData(
|
||||||
|
concept = "",
|
||||||
|
nuyen = 0,
|
||||||
|
essence = 6.0f,
|
||||||
|
name = "New Character",
|
||||||
|
metatype = Metatype.Human,
|
||||||
|
age = 0,
|
||||||
|
gender = "",
|
||||||
|
streetCred = 0,
|
||||||
|
notoriety = 0,
|
||||||
|
publicAwareness = 0,
|
||||||
|
totalKarma = 0,
|
||||||
|
currentKarma = 0
|
||||||
|
),
|
||||||
|
damageMonitor = createDamageMonitor(DEFAULT_ATTRIBUTES),
|
||||||
|
)
|
||||||
|
|
||||||
val EXAMPLE_CHARACTER: ShadowrunCharacter = ShadowrunCharacter(
|
val EXAMPLE_CHARACTER: ShadowrunCharacter = ShadowrunCharacter(
|
||||||
attributes = EXAMPLE_ATTRIBUTES,
|
attributes = EXAMPLE_ATTRIBUTES,
|
||||||
talents = Talents(createAllProvidedTalents()),
|
talents = Talents(createAllProvidedTalents()),
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package org.shahondin1624
|
||||||
|
|
||||||
|
import org.shahondin1624.model.characterdata.Metatype
|
||||||
|
import org.shahondin1624.model.createNewCharacter
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class NewCharacterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasDefaultName() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
assertEquals("New Character", char.characterData.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasAllAttributesAtOne() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
val attrs = char.attributes.getAllAttributes()
|
||||||
|
for (attr in attrs) {
|
||||||
|
assertEquals(1, attr.value, "Attribute ${attr.type.name} should be 1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasEdgeOne() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
assertEquals(1, char.attributes.edge)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasAllTalentsAtZero() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
for (talent in char.talents.talents) {
|
||||||
|
assertEquals(0, talent.value, "Talent ${talent.name} should be 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasZeroKarma() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
assertEquals(0, char.characterData.totalKarma)
|
||||||
|
assertEquals(0, char.characterData.currentKarma)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasZeroNuyen() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
assertEquals(0, char.characterData.nuyen)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasFullEssence() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
assertEquals(6.0f, char.characterData.essence)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterIsHumanByDefault() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
assertEquals(Metatype.Human, char.characterData.metatype)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newCharacterHasZeroReputation() {
|
||||||
|
val char = createNewCharacter()
|
||||||
|
assertEquals(0, char.characterData.streetCred)
|
||||||
|
assertEquals(0, char.characterData.notoriety)
|
||||||
|
assertEquals(0, char.characterData.publicAwareness)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user