mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 08:27:04 +00:00
refactor(desktop): provide blossom servers via LocalBlossomServers
Replace the threaded blossomServers parameters with a LocalBlossomServers CompositionLocal, provided once from the account's state holder (iAccount.blossomServerList.flow) around the logged-in UI. Upload sites read the list from context instead of receiving it down a parameter chain. - Provide LocalBlossomServers in MainContent's existing provider (covers feeds, chats, profile, settings) and around the top-level compose dialog. - ComposeNoteDialog, EditProfileDialog, ChatPane and the media-server settings section read LocalBlossomServers.current; drop the params and the prop-drilling through UserProfileScreen and DesktopMessagesScreen. - This also covers quote-compose from a feed row (NoteActionsRow), which the parameter approach couldn't reach — the per-note card composables now get the list from context, so it no longer defaults to the primal server. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011dkzkEY6cUsRfqEb7giHi2
This commit is contained in:
@@ -112,6 +112,7 @@ import com.vitorpamplona.amethyst.desktop.subscriptions.DesktopRelaySubscription
|
||||
import com.vitorpamplona.amethyst.desktop.ui.ComposeNoteDialog
|
||||
import com.vitorpamplona.amethyst.desktop.ui.ConnectingRelaysScreen
|
||||
import com.vitorpamplona.amethyst.desktop.ui.ImportFollowListDialog
|
||||
import com.vitorpamplona.amethyst.desktop.ui.LocalBlossomServers
|
||||
import com.vitorpamplona.amethyst.desktop.ui.LoginScreen
|
||||
import com.vitorpamplona.amethyst.desktop.ui.ZapFeedback
|
||||
import com.vitorpamplona.amethyst.desktop.ui.auth.ForceLogoutDialog
|
||||
@@ -1471,19 +1472,23 @@ private fun AppInner(
|
||||
}
|
||||
}
|
||||
|
||||
// Compose dialog
|
||||
// Compose dialog. Hosted outside MainContent's provider,
|
||||
// so provide the account's blossom list here too.
|
||||
if (showComposeDialog) {
|
||||
ComposeNoteDialog(
|
||||
onDismiss = onDismissComposeDialog,
|
||||
relayManager = relayManager,
|
||||
account = account,
|
||||
localCache = localCache,
|
||||
blossomServers = iAccount.blossomServerList.flow,
|
||||
replyTo = replyToNote,
|
||||
draftDTag = composeEditDraftTag,
|
||||
draftInitialContent = composeEditContent,
|
||||
initialScheduledForSec = composeEditScheduledForSec,
|
||||
)
|
||||
CompositionLocalProvider(
|
||||
LocalBlossomServers provides iAccount.blossomServerList.flow,
|
||||
) {
|
||||
ComposeNoteDialog(
|
||||
onDismiss = onDismissComposeDialog,
|
||||
relayManager = relayManager,
|
||||
account = account,
|
||||
localCache = localCache,
|
||||
replyTo = replyToNote,
|
||||
draftDTag = composeEditDraftTag,
|
||||
draftInitialContent = composeEditContent,
|
||||
initialScheduledForSec = composeEditScheduledForSec,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// App Drawer overlay
|
||||
@@ -1972,6 +1977,7 @@ fun MainContent(
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalRelayCategories provides relayCategories,
|
||||
LocalBlossomServers provides iAccount.blossomServerList.flow,
|
||||
com.vitorpamplona.amethyst.desktop.ui.relay.LocalAccountRelays provides accountRelays,
|
||||
com.vitorpamplona.amethyst.desktop.ui.deck.LocalDesktopCache provides localCache,
|
||||
com.vitorpamplona.amethyst.desktop.ui.deck.LocalRelayManager provides relayManager,
|
||||
@@ -2225,7 +2231,6 @@ fun RelaySettingsScreen(
|
||||
.TorSettings(torType = com.vitorpamplona.amethyst.commons.tor.TorType.OFF),
|
||||
onTorSettingsChanged: (com.vitorpamplona.amethyst.commons.tor.TorSettings) -> Unit = {},
|
||||
namecoinPreferences: DesktopNamecoinPreferences? = null,
|
||||
blossomServers: kotlinx.coroutines.flow.StateFlow<List<String>>? = null,
|
||||
onBlossomServersChanged: (List<String>) -> Unit = {},
|
||||
) {
|
||||
val relayStatuses by relayManager.relayStatuses.collectAsState()
|
||||
@@ -2351,7 +2356,7 @@ fun RelaySettingsScreen(
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
// Media Server Settings (Blossom, kind 10063 — synced with mobile)
|
||||
val networkBlossomServers by (blossomServers?.collectAsState() ?: remember { mutableStateOf(emptyList<String>()) })
|
||||
val networkBlossomServers by (LocalBlossomServers.current?.collectAsState() ?: remember { mutableStateOf(emptyList<String>()) })
|
||||
// The kind-10063 list is authoritative; before it loads (or when the
|
||||
// user has published none) show the default server.
|
||||
val effectiveBlossomServers = networkBlossomServers.ifEmpty { listOf(DEFAULT_BLOSSOM_SERVER) }
|
||||
|
||||
+3
-4
@@ -118,7 +118,6 @@ import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.isClient
|
||||
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.awt.datatransfer.DataFlavor
|
||||
@@ -140,7 +139,6 @@ fun ComposeNoteDialog(
|
||||
relayManager: DesktopRelayConnectionManager,
|
||||
account: AccountState.LoggedIn,
|
||||
localCache: com.vitorpamplona.amethyst.desktop.cache.DesktopLocalCache? = null,
|
||||
blossomServers: StateFlow<List<String>>? = null,
|
||||
replyTo: Event? = null,
|
||||
quoteOf: Event? = null,
|
||||
draftDTag: String? = null,
|
||||
@@ -208,8 +206,9 @@ fun ComposeNoteDialog(
|
||||
val uploadState by uploadTracker.state.collectAsState()
|
||||
val orchestrator = remember { UploadOrchestrator() }
|
||||
// Media servers come straight from the account's kind-10063 list holder
|
||||
// (account.blossomServerList.flow), reactively — no cache-poking, no prefs.
|
||||
val serverList by (blossomServers?.collectAsState() ?: remember { mutableStateOf(emptyList<String>()) })
|
||||
// (account.blossomServerList.flow), provided via LocalBlossomServers —
|
||||
// reactively, no cache-poking, no prefs.
|
||||
val serverList by (LocalBlossomServers.current?.collectAsState() ?: remember { mutableStateOf(emptyList<String>()) })
|
||||
val effectiveServers = serverList.ifEmpty { listOf(DEFAULT_BLOSSOM_SERVER) }
|
||||
var selectedServer by remember { mutableStateOf(effectiveServers.first()) }
|
||||
// If the list loads (or changes) after the dialog opens and the current pick
|
||||
|
||||
@@ -1043,7 +1043,6 @@ fun FeedScreen(
|
||||
relayManager = relayManager,
|
||||
account = account,
|
||||
localCache = localCache,
|
||||
blossomServers = iAccount?.blossomServerList?.flow,
|
||||
replyTo = replyToEvent,
|
||||
)
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.desktop.ui
|
||||
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* The logged-in account's Blossom media server list (NIP-B7 / kind 10063),
|
||||
* exposed as the reactive flow from the account's `BlossomServerListState`
|
||||
* (`iAccount.blossomServerList.flow`). Provided once around the logged-in UI so
|
||||
* upload composables read the list from context instead of threading it through
|
||||
* every screen. `null` when no account is logged in — consumers fall back to
|
||||
* [com.vitorpamplona.amethyst.desktop.model.DEFAULT_BLOSSOM_SERVER].
|
||||
*/
|
||||
val LocalBlossomServers = staticCompositionLocalOf<StateFlow<List<String>>?> { null }
|
||||
@@ -1376,8 +1376,6 @@ fun NoteActionsRow(
|
||||
relayManager = relayManager,
|
||||
account = account,
|
||||
localCache = localCache,
|
||||
// Quote-compose from a feed row falls back to the default media server:
|
||||
// the per-note card composables don't carry the account's blossom flow.
|
||||
quoteOf = quoteEvent,
|
||||
)
|
||||
}
|
||||
|
||||
-2
@@ -122,7 +122,6 @@ fun UserProfileScreen(
|
||||
relayManager: DesktopRelayConnectionManager,
|
||||
localCache: DesktopLocalCache,
|
||||
account: AccountState.LoggedIn?,
|
||||
blossomServers: kotlinx.coroutines.flow.StateFlow<List<String>>? = null,
|
||||
nwcConnection: com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect.Nip47URINorm? = null,
|
||||
subscriptionsCoordinator: DesktopRelaySubscriptionsCoordinator? = null,
|
||||
onBack: () -> Unit,
|
||||
@@ -1190,7 +1189,6 @@ fun UserProfileScreen(
|
||||
EditProfileDialog(
|
||||
account = account,
|
||||
relayManager = relayManager,
|
||||
blossomServers = blossomServers,
|
||||
latestMetadata = latestMetadataEvent,
|
||||
latestIdentities = latestIdentitiesEvent,
|
||||
onDismiss = { showEditProfile = false },
|
||||
|
||||
+3
-1
@@ -89,6 +89,7 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.ChatNewMessageState
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.ChatroomFeedViewModel
|
||||
import com.vitorpamplona.amethyst.desktop.model.DEFAULT_BLOSSOM_SERVER
|
||||
import com.vitorpamplona.amethyst.desktop.ui.LocalBlossomServers
|
||||
import com.vitorpamplona.amethyst.desktop.ui.components.ToggleableTimeAgoText
|
||||
import com.vitorpamplona.amethyst.desktop.ui.media.DesktopFilePicker
|
||||
import com.vitorpamplona.amethyst.desktop.ui.media.MediaAttachmentRow
|
||||
@@ -136,13 +137,14 @@ fun ChatPane(
|
||||
cacheProvider: ICacheProvider,
|
||||
feedViewModel: ChatroomFeedViewModel,
|
||||
messageState: ChatNewMessageState,
|
||||
blossomServers: StateFlow<List<String>>? = null,
|
||||
dmBroadcastStatus: DmBroadcastStatus = DmBroadcastStatus.Idle,
|
||||
onNavigateToProfile: (String) -> Unit = {},
|
||||
onBack: (() -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
// Account's Blossom media servers (kind 10063), read from context.
|
||||
val blossomServers = LocalBlossomServers.current
|
||||
val feedState by feedViewModel.feedState.feedContent.collectAsState()
|
||||
val messageText by messageState.message.collectAsState()
|
||||
val recipientsMissingRelays by messageState.recipientsMissingDmRelays.collectAsState()
|
||||
|
||||
-2
@@ -271,7 +271,6 @@ private fun CompactMessagesContent(
|
||||
cacheProvider = cacheProvider,
|
||||
feedViewModel = feedViewModel,
|
||||
messageState = messageState,
|
||||
blossomServers = (account as? DesktopIAccount)?.blossomServerList?.flow,
|
||||
dmBroadcastStatus = broadcastStatus,
|
||||
onNavigateToProfile = onNavigateToProfile,
|
||||
onBack = { listState.clearSelection() },
|
||||
@@ -376,7 +375,6 @@ private fun SplitMessagesContent(
|
||||
cacheProvider = cacheProvider,
|
||||
feedViewModel = feedViewModel,
|
||||
messageState = messageState,
|
||||
blossomServers = (account as? DesktopIAccount)?.blossomServerList?.flow,
|
||||
dmBroadcastStatus = broadcastStatus,
|
||||
onNavigateToProfile = onNavigateToProfile,
|
||||
)
|
||||
|
||||
-4
@@ -472,7 +472,6 @@ internal fun RootContent(
|
||||
relayManager = relayManager,
|
||||
localCache = localCache,
|
||||
account = account,
|
||||
blossomServers = iAccount.blossomServerList.flow,
|
||||
nwcConnection = nwcConnection,
|
||||
subscriptionsCoordinator = subscriptionsCoordinator,
|
||||
onBack = {},
|
||||
@@ -503,7 +502,6 @@ internal fun RootContent(
|
||||
torSettings = torState.settings,
|
||||
onTorSettingsChanged = torState.onSettingsChanged,
|
||||
namecoinPreferences = LocalNamecoinPreferences.current,
|
||||
blossomServers = iAccount.blossomServerList.flow,
|
||||
onBlossomServersChanged = { servers ->
|
||||
// Publish a kind-10063 event so the list syncs to every
|
||||
// Amethyst client, then consume it locally so state updates
|
||||
@@ -554,7 +552,6 @@ internal fun RootContent(
|
||||
relayManager = relayManager,
|
||||
localCache = localCache,
|
||||
account = account,
|
||||
blossomServers = iAccount.blossomServerList.flow,
|
||||
nwcConnection = nwcConnection,
|
||||
subscriptionsCoordinator = subscriptionsCoordinator,
|
||||
onBack = {},
|
||||
@@ -707,7 +704,6 @@ internal fun OverlayContent(
|
||||
relayManager = relayManager,
|
||||
localCache = localCache,
|
||||
account = account,
|
||||
blossomServers = iAccount?.blossomServerList?.flow,
|
||||
nwcConnection = nwcConnection,
|
||||
subscriptionsCoordinator = subscriptionsCoordinator,
|
||||
onBack = onBack,
|
||||
|
||||
+2
-3
@@ -82,6 +82,7 @@ import com.vitorpamplona.amethyst.commons.service.upload.UploadOrchestrator
|
||||
import com.vitorpamplona.amethyst.desktop.account.AccountState
|
||||
import com.vitorpamplona.amethyst.desktop.model.DEFAULT_BLOSSOM_SERVER
|
||||
import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager
|
||||
import com.vitorpamplona.amethyst.desktop.ui.LocalBlossomServers
|
||||
import com.vitorpamplona.amethyst.desktop.ui.media.DesktopFilePicker
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Client
|
||||
@@ -92,7 +93,6 @@ import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
@@ -125,7 +125,6 @@ sealed class Nip05Status {
|
||||
fun EditProfileDialog(
|
||||
account: AccountState.LoggedIn,
|
||||
relayManager: DesktopRelayConnectionManager,
|
||||
blossomServers: StateFlow<List<String>>? = null,
|
||||
latestMetadata: MetadataEvent?,
|
||||
latestIdentities: ExternalIdentitiesEvent?,
|
||||
onDismiss: () -> Unit,
|
||||
@@ -173,7 +172,7 @@ fun EditProfileDialog(
|
||||
}
|
||||
|
||||
val orchestrator = remember { UploadOrchestrator() }
|
||||
val serverBaseUrl = blossomServers?.value?.firstOrNull() ?: DEFAULT_BLOSSOM_SERVER
|
||||
val serverBaseUrl = LocalBlossomServers.current?.value?.firstOrNull() ?: DEFAULT_BLOSSOM_SERVER
|
||||
|
||||
fun uploadFile(
|
||||
file: File,
|
||||
|
||||
Reference in New Issue
Block a user