mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 11:48:24 +00:00
fix: fetch uncached addressable thread roots; render blank PS1 blocks as empty slots
This commit is contained in:
@@ -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?,
|
||||
)
|
||||
|
||||
+5
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
+7
@@ -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
|
||||
}
|
||||
|
||||
+28
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user