From 5f3848c47d71652a22de7256a2894e66c2c72d61 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 23:20:54 +0000 Subject: [PATCH] feat: allow single-char (continent) geohash precision in location channels Add a CONTINENT(1) level to GeohashChannelLevel so the coarsest selectable geohash channel is a single character (~5000 km, one of 32 cells for the globe), previously floored at REGION(2). Every precision picker iterates GeohashChannelLevel.ordered, so the map picker, Teleport, "Near me" list and New-geohash-chat all pick it up automatically; GeohashChatsScreen now labels a 1-char cell "Continent" instead of showing no level. Adds the "Continent" label and "~5000 km" chip subtitle, and extends the GeohashChannelLevel test coverage. REGION..BUILDING (2-8 chars) still mirror the Bitchat channel levels; CONTINENT is an Amethyst extension beyond them. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Vtvo2iNSnG3M21QwbFPznU --- .../creators/location/GeohashLocationPickerDialog.kt | 1 + .../chats/geohashChat/NewGeohashChatScreen.kt | 1 + .../bitchat/geohash/GeohashChannelLevel.kt | 12 +++++++++--- .../experimental/bitchat/GeohashChannelLevelTest.kt | 5 ++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/location/GeohashLocationPickerDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/location/GeohashLocationPickerDialog.kt index 30dd9450fd..65ced016f5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/location/GeohashLocationPickerDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/location/GeohashLocationPickerDialog.kt @@ -766,6 +766,7 @@ private fun geohashBounds(geohash: String): BoundingBox? { /** A rough physical size for a geohash cell at this precision, for the chip subtitle. */ private fun GeohashChannelLevel.areaSize(): String = when (this) { + GeohashChannelLevel.CONTINENT -> "~5000 km" GeohashChannelLevel.REGION -> "~1250 km" GeohashChannelLevel.PROVINCE -> "~39 km" GeohashChannelLevel.CITY -> "~5 km" diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/NewGeohashChatScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/NewGeohashChatScreen.kt index 30a774338f..d51f1fe337 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/NewGeohashChatScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/NewGeohashChatScreen.kt @@ -377,6 +377,7 @@ private fun TeleportCard(onClick: () -> Unit) { internal fun GeohashChannelLevel.label(): String = when (this) { + GeohashChannelLevel.CONTINENT -> "Continent" GeohashChannelLevel.REGION -> "Region" GeohashChannelLevel.PROVINCE -> "Province" GeohashChannelLevel.CITY -> "City" diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/bitchat/geohash/GeohashChannelLevel.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/bitchat/geohash/GeohashChannelLevel.kt index 9ea0b2c0b5..a326e730d2 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/bitchat/geohash/GeohashChannelLevel.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/bitchat/geohash/GeohashChannelLevel.kt @@ -21,8 +21,13 @@ package com.vitorpamplona.quartz.experimental.bitchat.geohash /** - * The named geohash precision levels Bitchat exposes as location channels, each a - * fixed geohash character length. Coarser (fewer chars) = larger area. + * The named geohash precision levels exposed as location channels, each a fixed + * geohash character length. Coarser (fewer chars) = larger area. + * + * [REGION]…[BUILDING] (2–8 chars) mirror the levels Bitchat exposes, so those cells + * interoperate with Bitchat peers. [CONTINENT] (1 char) is an Amethyst extension: a + * single character splits the globe into 32 ~5000 km cells, coarser than any Bitchat + * channel, for a whole-continent room. * * Because a geohash is a prefix code, the channel for any level is just the first * [chars] characters of a finer cell — so one precise fix yields every level via @@ -31,6 +36,7 @@ package com.vitorpamplona.quartz.experimental.bitchat.geohash enum class GeohashChannelLevel( val chars: Int, ) { + CONTINENT(1), REGION(2), PROVINCE(4), CITY(5), @@ -47,7 +53,7 @@ enum class GeohashChannelLevel( companion object { /** Coarsest → finest, the order a location-channel picker should list them. */ - val ordered = listOf(REGION, PROVINCE, CITY, NEIGHBORHOOD, BLOCK, BUILDING) + val ordered = listOf(CONTINENT, REGION, PROVINCE, CITY, NEIGHBORHOOD, BLOCK, BUILDING) /** The named level whose precision matches [chars], if any. */ fun forChars(chars: Int): GeohashChannelLevel? = entries.firstOrNull { it.chars == chars } diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/bitchat/GeohashChannelLevelTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/bitchat/GeohashChannelLevelTest.kt index dba93500f7..9285a21c20 100644 --- a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/bitchat/GeohashChannelLevelTest.kt +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/bitchat/GeohashChannelLevelTest.kt @@ -28,6 +28,7 @@ import kotlin.test.assertNull class GeohashChannelLevelTest { @Test fun charCountsMatchBitchat() { + assertEquals(1, GeohashChannelLevel.CONTINENT.chars) assertEquals(2, GeohashChannelLevel.REGION.chars) assertEquals(4, GeohashChannelLevel.PROVINCE.chars) assertEquals(5, GeohashChannelLevel.CITY.chars) @@ -39,6 +40,7 @@ class GeohashChannelLevelTest { @Test fun cellForTruncatesToLevel() { val fix = "u4pruydq" // 8 chars + assertEquals("u", GeohashChannelLevel.CONTINENT.cellFor(fix)) assertEquals("u4", GeohashChannelLevel.REGION.cellFor(fix)) assertEquals("u4pr", GeohashChannelLevel.PROVINCE.cellFor(fix)) assertEquals("u4pru", GeohashChannelLevel.CITY.cellFor(fix)) @@ -55,11 +57,12 @@ class GeohashChannelLevelTest { @Test fun orderedIsCoarseToFine() { - assertEquals(listOf(2, 4, 5, 6, 7, 8), GeohashChannelLevel.ordered.map { it.chars }) + assertEquals(listOf(1, 2, 4, 5, 6, 7, 8), GeohashChannelLevel.ordered.map { it.chars }) } @Test fun forCharsResolvesNamedLevels() { + assertEquals(GeohashChannelLevel.CONTINENT, GeohashChannelLevel.forChars(1)) assertEquals(GeohashChannelLevel.CITY, GeohashChannelLevel.forChars(5)) assertNull(GeohashChannelLevel.forChars(3)) }