From 3bd490b58c8aeb1975144afcf07749522f551b3e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 22:11:44 +0000 Subject: [PATCH] feat: pin followed geohash channels to the bottom nav MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a "Location Channels" category to the bottom-bar settings picker so a user can pin their joined geohash cells as avatars alongside PublicChat, RelayGroup and Concord groups. Each pinned cell renders a location-pin robohash keyed by the geohash, labelled "#", opening the location chat when tapped. - BottomBarEntry.Geohash sealed variant (+ stable key) - NavBarItem.GEOHASH_CHATS catalog entry in the chats category - rememberGeohashEntryDisplay resolves the anonymous avatar/route (no metadata REQ — the cell is anonymous by design) - picker/settings and live-bar group branches route geohash through the shared GroupEntryDisplay path - no feed preloader (the cell's message subscription opens from the chat screen itself) Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV --- .../ui/navigation/bottombars/AppBottomBar.kt | 1 + .../ui/navigation/bottombars/BottomBarEntry.kt | 8 ++++++++ .../bottombars/GroupBottomBarEntries.kt | 16 ++++++++++++++++ .../ui/navigation/bottombars/NavBarItem.kt | 9 +++++++++ .../screen/loggedIn/BottomBarFeedPreloaders.kt | 4 ++++ .../loggedIn/settings/BottomBarSettingsScreen.kt | 9 +++++++++ amethyst/src/main/res/values/strings.xml | 1 + 7 files changed, 48 insertions(+) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt index dedc9b5521..3609933d2c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/AppBottomBar.kt @@ -236,6 +236,7 @@ internal fun rememberBottomBarSlot( is BottomBarEntry.PublicChat, is BottomBarEntry.RelayGroup, is BottomBarEntry.Concord, + is BottomBarEntry.Geohash, -> { val display = rememberGroupEntryDisplay(entry, accountViewModel) ?: return null // A pinned chat/group shows its avatar, like the favorite-app tabs — icon only. diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/BottomBarEntry.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/BottomBarEntry.kt index 6024211f8b..9a8615bb5f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/BottomBarEntry.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/BottomBarEntry.kt @@ -76,6 +76,13 @@ sealed interface BottomBarEntry { data class Concord( val communityId: String, ) : BottomBarEntry + + /** A pinned Bitchat geohash location channel, keyed by its geohash cell; opens the location chat. */ + @Serializable + @SerialName("geohash") + data class Geohash( + val geohash: String, + ) : BottomBarEntry } /** @@ -90,6 +97,7 @@ val BottomBarEntry.stableKey: String is BottomBarEntry.PublicChat -> "publicChat:$channelId" is BottomBarEntry.RelayGroup -> "relayGroup:$relayUrl|$groupId" is BottomBarEntry.Concord -> "concord:$communityId" + is BottomBarEntry.Geohash -> "geohash:$geohash" } /** The favorite-app ids in this bottom-bar config — the apps that should be kept warm as bottom-row tabs. */ diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt index 242dcf9eef..9007e1182a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt @@ -172,6 +172,21 @@ fun rememberConcordEntryDisplay( ) } +/** + * A pinned geohash location channel. Anonymous by design — no metadata REQ; the row is a location + * pin robohash keyed by the cell, labelled with the geohash, opening the location chat. + */ +@Composable +fun rememberGeohashEntryDisplay(entry: BottomBarEntry.Geohash): GroupEntryDisplay = + remember(entry.geohash) { + GroupEntryDisplay( + label = "#${entry.geohash}", + robotSeed = entry.geohash, + model = null, + route = Route.GeohashChat(entry.geohash), + ) + } + /** * Resolves any chat/group entry to its live display, or null for a non-group entry. [subscribe] is * forwarded to the channel observers: true (the default) keeps a relay REQ open — used by the live @@ -187,6 +202,7 @@ fun rememberGroupEntryDisplay( is BottomBarEntry.PublicChat -> rememberPublicChatEntryDisplay(entry, accountViewModel, subscribe) is BottomBarEntry.RelayGroup -> rememberRelayGroupEntryDisplay(entry, accountViewModel, subscribe) is BottomBarEntry.Concord -> rememberConcordEntryDisplay(entry, accountViewModel) + is BottomBarEntry.Geohash -> rememberGeohashEntryDisplay(entry) is BottomBarEntry.BuiltIn -> null is BottomBarEntry.Favorite -> null } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/NavBarItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/NavBarItem.kt index 5845fe237c..cc0033011d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/NavBarItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/NavBarItem.kt @@ -68,6 +68,7 @@ enum class NavBarItem { PUBLIC_CHATS, RELAY_GROUPS, CONCORD, + GEOHASH_CHATS, FOLLOW_PACKS, LIVE_STREAMS, NESTS, @@ -334,6 +335,13 @@ val NavBarCatalog: Map = icon = MaterialSymbols.Group, resolveRoute = { Route.Concords }, ), + NavBarItem.GEOHASH_CHATS to + NavBarItemDef( + id = NavBarItem.GEOHASH_CHATS, + labelRes = R.string.location_channels, + icon = MaterialSymbols.LocationOn, + resolveRoute = { Route.NewGeohashChat }, + ), NavBarItem.FOLLOW_PACKS to NavBarItemDef( id = NavBarItem.FOLLOW_PACKS, @@ -471,6 +479,7 @@ val BottomBarCategories: List = NavBarItem.PUBLIC_CHATS, NavBarItem.RELAY_GROUPS, NavBarItem.CONCORD, + NavBarItem.GEOHASH_CHATS, ), ), NavBarCategory( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BottomBarFeedPreloaders.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BottomBarFeedPreloaders.kt index a05ddaf6ac..69c6a2f3eb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BottomBarFeedPreloaders.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BottomBarFeedPreloaders.kt @@ -156,6 +156,10 @@ private fun PreloadFor( // Covered by AccountFilterAssemblerSubscription (always-on). NavBarItem.NOTIFICATIONS -> Unit + // Anonymous location channels — the pinned avatar carries no metadata, and the cell's + // message subscription opens from the chat screen itself, so there's nothing to preload. + NavBarItem.GEOHASH_CHATS -> Unit + // No relay feed to preload (loaded by the destination screen on demand). NavBarItem.PROFILE, NavBarItem.MY_LISTS, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/BottomBarSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/BottomBarSettingsScreen.kt index bd7fce2cef..05d91be6d6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/BottomBarSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/BottomBarSettingsScreen.kt @@ -107,6 +107,7 @@ private val ExpandableItems = NavBarItem.PUBLIC_CHATS, NavBarItem.RELAY_GROUPS, NavBarItem.CONCORD, + NavBarItem.GEOHASH_CHATS, ) /** Soft guidance, not a hard cap: a Material bottom bar reads best at ~5 tabs. */ @@ -588,6 +589,13 @@ private fun PickerChildren( GroupChildList(entries, pinnedKeys, accountViewModel, onTogglePin) } + NavBarItem.GEOHASH_CHATS -> { + val cells by accountViewModel.account.geohashList.flow + .collectAsStateWithLifecycle() + val entries = remember(cells) { cells.sorted().map { BottomBarEntry.Geohash(it) } } + GroupChildList(entries, pinnedKeys, accountViewModel, onTogglePin) + } + else -> {} } } @@ -828,6 +836,7 @@ private fun rememberPinnedVisual( is BottomBarEntry.PublicChat, is BottomBarEntry.RelayGroup, is BottomBarEntry.Concord, + is BottomBarEntry.Geohash, -> { // Read-only: the settings list resolves from cache; the live bar owns the subscription. val display = rememberGroupEntryDisplay(entry, accountViewModel, subscribe = false) diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 041113c279..1bf7700459 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -962,6 +962,7 @@ Hide older releases Shorts Public Chats + Location Channels Follow Packs Live Streams Nests