fix: WASM/JS compatibility — replace JVM-only APIs with multiplatform alternatives

String.format(), toSortedMap(), and @Volatile are not available on WASM/JS
targets. Replace with multiplatform-safe formatFloat() helper, entries.sortedBy(),
and remove the unnecessary @Volatile annotation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
shahondin1624
2026-04-07 08:09:17 +02:00
parent c17adb4936
commit f6cfef6ea3
6 changed files with 31 additions and 14 deletions
@@ -17,6 +17,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.functions.formatFloat
import org.shahondin1624.lib.components.UiConstants
import org.shahondin1624.model.magic.AdeptPower
import org.jetbrains.compose.resources.stringResource
@@ -112,7 +113,7 @@ fun AdeptPowerPanel(
fontWeight = FontWeight.Bold
)
Text(
text = "%.1f / %.1f PP".format(totalPowerPointsUsed, powerPointBudget),
text = "${formatFloat(totalPowerPointsUsed, 1)} / ${formatFloat(powerPointBudget, 1)} PP",
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold,
color = if (totalPowerPointsUsed > powerPointBudget) MaterialTheme.colorScheme.error
@@ -181,7 +182,7 @@ private fun AdeptPowerEntryRow(
}
}
Text(
text = "%.1f PP".format(power.powerPointCost),
text = "${formatFloat(power.powerPointCost, 1)} PP",
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(horizontal = 8.dp)
@@ -18,6 +18,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.components.UiConstants
import org.shahondin1624.lib.functions.formatFloat
import org.shahondin1624.model.attributes.AttributeType
import org.shahondin1624.model.characterdata.Augmentation
import org.shahondin1624.model.characterdata.AugmentationGrade
@@ -139,7 +140,7 @@ fun AugmentationPanel(
fontWeight = FontWeight.Bold
)
Text(
text = "%.2f".format(totalEssenceCost),
text = formatFloat(totalEssenceCost, 2),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.error
@@ -160,7 +161,7 @@ fun AugmentationPanel(
fontWeight = FontWeight.Bold
)
Text(
text = "%.2f / %.1f".format(currentEssence, baseEssence),
text = "${formatFloat(currentEssence, 2)} / ${formatFloat(baseEssence, 1)}",
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold,
color = if (currentEssence <= 0f) MaterialTheme.colorScheme.error
@@ -212,7 +213,7 @@ private fun AugmentationEntryRow(
}
}
Text(
text = "%.2f".format(augmentation.essenceCost),
text = formatFloat(augmentation.essenceCost, 2),
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(horizontal = 8.dp)
@@ -379,7 +380,7 @@ fun AugmentationEditDialog(
// Show computed essence cost
if (essenceCost != null) {
Text(
text = "Effective cost: ${"%.2f".format(essenceCost * grade.essenceMultiplier)}",
text = "Effective cost: ${formatFloat(essenceCost * grade.essenceMultiplier, 2)}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -269,7 +269,7 @@ private fun AttributesSummaryCard(state: CharacterCreationState) {
@Composable
private fun SkillsSummaryCard(state: CharacterCreationState) {
val allocatedSkills = state.skillAllocations.filter { it.value > 0 }.toSortedMap()
val allocatedSkills = state.skillAllocations.filter { it.value > 0 }.entries.sortedBy { it.key }
Card(
modifier = Modifier.fillMaxWidth(),
@@ -76,7 +76,7 @@ fun SkillAllocationStep(
// Group skills by attribute
val grouped = allTalents.groupBy { it.attribute }
for ((attrType, talents) in grouped.toSortedMap(compareBy { it.name })) {
for ((attrType, talents) in grouped.entries.sortedBy { it.key.name }) {
Text(
text = attrType.name,
style = MaterialTheme.typography.titleMedium,
@@ -8,9 +8,25 @@ import kotlin.math.roundToInt
*
* Examples: 5.7f -> "5.7", 6.0f -> "6.0", 0.0f -> "0.0"
*/
fun formatEssence(value: Float): String {
val rounded = (value * 10).roundToInt() / 10.0
val intPart = rounded.toInt()
val decPart = ((rounded - intPart) * 10).roundToInt()
return "$intPart.$decPart"
fun formatEssence(value: Float): String = formatFloat(value, 1)
/**
* Formats a float to [decimals] decimal places without platform-specific String.format.
* Works on all KMP targets (JVM, JS, WASM, iOS).
*
* Examples: formatFloat(3.14159f, 2) -> "3.14", formatFloat(5.0f, 1) -> "5.0"
*/
fun formatFloat(value: Float, decimals: Int): String {
val factor = pow10(decimals)
val rounded = (value * factor).roundToInt()
val intPart = rounded / factor.toInt()
val decPart = kotlin.math.abs(rounded % factor.toInt())
val sign = if (value < 0 && intPart == 0) "-" else ""
return "$sign$intPart.${decPart.toString().padStart(decimals, '0')}"
}
private fun pow10(n: Int): Float {
var result = 1f
repeat(n) { result *= 10f }
return result
}
@@ -15,7 +15,6 @@ import io.github.aakira.napier.Napier
*/
object AppLogger {
@Volatile
private var initialized = false
/**