fix: fetch uncached addressable thread roots; render blank PS1 blocks as empty slots

This commit is contained in:
davotoula
2026-07-06 00:35:58 +02:00
parent 836caa5cd6
commit 9af611faf1
5 changed files with 53 additions and 2 deletions
@@ -43,6 +43,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
@@ -86,6 +87,7 @@ fun RenderPs1Save(baseNote: Note) {
region = noteEvent.region(),
blockNumber = noteEvent.blockNumber(),
icon = noteEvent.icon(),
isBlank = noteEvent.isBlankBlock(),
hexPreview =
noteEvent.content
.take(HEX_PREVIEW_CHARS)
@@ -127,7 +129,15 @@ fun RenderPs1Save(baseNote: Note) {
)
}
if (save.hexPreview != null) {
if (save.isBlank) {
Spacer(Modifier.height(6.dp))
Text(
text = stringRes(R.string.ps1_save_empty_slot),
style = MaterialTheme.typography.bodyMedium,
fontStyle = FontStyle.Italic,
color = MaterialTheme.colorScheme.placeholderText,
)
} else if (save.hexPreview != null) {
Spacer(Modifier.height(6.dp))
Text(
text = save.hexPreview,
@@ -187,5 +197,6 @@ private class Ps1SaveInfo(
val region: String?,
val blockNumber: Int?,
val icon: Ps1SaveIcon?,
val isBlank: Boolean,
val hexPreview: String?,
)
@@ -55,7 +55,11 @@ fun filterMissingEventsForThread(
mapOfSet {
val rootNote = threadInfo.root
if (rootNote.event == null && rootNote is AddressableNote) {
potentialRelaysToFindEvent(rootNote).ifEmpty { defaultRelays }.forEach { relayUrl ->
// Must be the address-based resolver: the event-based one feeds the
// note's aTag idHex into the hex-keyed event-hint index, which throws
// on the non-hex string and kills the whole filter build — leaving a
// thread opened on an uncached naddr permanently unfetched.
potentialRelaysToFindAddress(rootNote).ifEmpty { defaultRelays }.forEach { relayUrl ->
add(relayUrl, rootNote.address)
}
}
+1
View File
@@ -3498,6 +3498,7 @@
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
<string name="ps1_save_title">PS1 memory card save</string>
<string name="ps1_save_block">block %1$d</string>
<string name="ps1_save_empty_slot">Empty slot</string>
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
<string name="road_event_police">Police</string>
<string name="road_event_speed_camera">Speed camera</string>
@@ -83,6 +83,13 @@ class Ps1SaveEvent(
*/
fun icon() = Ps1SaveIcon.parse(content)
/**
* True when the block holds no save data: erased flash sectors (all `ff`)
* or zero-filled blocks, whatever the publisher's hex casing. UIs show
* these as empty slots instead of a hex dump.
*/
fun isBlankBlock() = content.isNotEmpty() && content.all { it.equals(content[0], ignoreCase = true) }
companion object {
const val KIND = 38192
}
@@ -24,6 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.utils.EventFactory
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertIs
import kotlin.test.assertTrue
@@ -77,6 +78,33 @@ class Ps1SaveEventTest {
assertEquals("synctest-f1fc95d1-1", event.dTag())
}
@Test
fun blankBlockDetection() {
fun eventWith(content: String): Ps1SaveEvent {
val event: Event =
EventFactory.create(
id = "00".repeat(32),
pubKey = "00".repeat(32),
createdAt = 1_783_151_940L,
kind = Ps1SaveEvent.KIND,
tags = arrayOf(arrayOf("d", "card-1")),
content = content,
sig = "00".repeat(64),
)
assertIs<Ps1SaveEvent>(event)
return event
}
// Erased flash sectors (all ff) and zero-filled blocks are both blank,
// regardless of the publisher's hex casing.
assertTrue(eventWith("ffffffffffff").isBlankBlock())
assertTrue(eventWith("ffFFffFFffFF").isBlankBlock())
assertTrue(eventWith("000000000000").isBlankBlock())
// Real save data and empty content are not.
assertFalse(eventWith("5343110153594e43").isBlankBlock())
assertFalse(eventWith("").isBlankBlock())
}
@Test
fun blockNumberIsNullWhenTagIsMissingOrNotANumber() {
val event: Event =