Moves media server from Dialog to Screen with full route.

This commit is contained in:
Vitor Pamplona
2025-07-11 09:22:27 -04:00
parent 2ecfeb6e7f
commit e37d6ebd8b
11 changed files with 307 additions and 166 deletions

View File

@@ -27,7 +27,6 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.NoteState import com.vitorpamplona.amethyst.model.NoteState
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
import com.vitorpamplona.quartz.nipB7Blossom.BlossomAuthorizationEvent import com.vitorpamplona.quartz.nipB7Blossom.BlossomAuthorizationEvent
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
import com.vitorpamplona.quartz.utils.tryAndWait import com.vitorpamplona.quartz.utils.tryAndWait
@@ -49,14 +48,14 @@ class BlossomServerListState(
) { ) {
fun getBlossomServersAddress() = BlossomServersEvent.createAddress(signer.pubKey) fun getBlossomServersAddress() = BlossomServersEvent.createAddress(signer.pubKey)
fun getBlossomServersNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(getBlossomServersAddress()) fun getBlossomServersNote(): AddressableNote = cache.getOrCreateAddressableNote(getBlossomServersAddress())
fun getBlossomServersListFlow(): StateFlow<NoteState> = getBlossomServersNote().flow().metadata.stateFlow fun getBlossomServersListFlow(): StateFlow<NoteState> = getBlossomServersNote().flow().metadata.stateFlow
fun getBlossomServersList(): BlossomServersEvent? = getBlossomServersNote().event as? BlossomServersEvent fun getBlossomServersList(): BlossomServersEvent? = getBlossomServersNote().event as? BlossomServersEvent
fun normalizeServers(note: Note): List<String> { fun normalizeServers(note: Note): List<String> {
val event = note.event as? FileServersEvent val event = note.event as? BlossomServersEvent
return event?.servers() ?: emptyList() return event?.servers() ?: emptyList()
} }

View File

@@ -45,59 +45,30 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.buttons.CloseButton import com.vitorpamplona.amethyst.ui.note.buttons.CloseButton
import com.vitorpamplona.amethyst.ui.note.buttons.SaveButton import com.vitorpamplona.amethyst.ui.note.buttons.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategoryWithButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategoryWithButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.DoubleVertPadding import com.vitorpamplona.amethyst.ui.theme.DoubleVertPadding
import com.vitorpamplona.amethyst.ui.theme.FeedPadding import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.HalfHorzPadding
import com.vitorpamplona.amethyst.ui.theme.SettingsCategoryFirstModifier import com.vitorpamplona.amethyst.ui.theme.SettingsCategoryFirstModifier
import com.vitorpamplona.amethyst.ui.theme.SettingsCategorySpacingModifier import com.vitorpamplona.amethyst.ui.theme.SettingsCategorySpacingModifier
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.grayText import com.vitorpamplona.amethyst.ui.theme.grayText
@Composable
fun MediaServersListView(
onClose: () -> Unit,
accountViewModel: AccountViewModel,
nav: INav,
) {
val nip96ServersViewModel: NIP96ServersViewModel = viewModel()
val blossomServersViewModel: BlossomServersViewModel = viewModel()
LaunchedEffect(key1 = Unit) {
nip96ServersViewModel.load(accountViewModel.account)
blossomServersViewModel.load(accountViewModel.account)
}
Dialog(
onDismissRequest = onClose,
properties = DialogProperties(usePlatformDefaultWidth = false, decorFitsSystemWindows = false),
) {
SetDialogToEdgeToEdge()
DialogContent(nip96ServersViewModel, blossomServersViewModel, onClose)
}
}
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun DialogContent( fun MediaServersListView(
nip96ServersViewModel: NIP96ServersViewModel, nip96ServersViewModel: NIP96ServersViewModel,
blossomServersViewModel: BlossomServersViewModel, blossomServersViewModel: BlossomServersViewModel,
onClose: () -> Unit, onClose: () -> Unit,
@@ -119,6 +90,7 @@ fun DialogContent(
}, },
navigationIcon = { navigationIcon = {
CloseButton( CloseButton(
modifier = HalfHorzPadding,
onPress = { onPress = {
nip96ServersViewModel.refresh() nip96ServersViewModel.refresh()
blossomServersViewModel.refresh() blossomServersViewModel.refresh()
@@ -128,6 +100,7 @@ fun DialogContent(
}, },
actions = { actions = {
SaveButton( SaveButton(
modifier = HalfHorzPadding,
onPost = { onPost = {
nip96ServersViewModel.saveFileServers() nip96ServersViewModel.saveFileServers()
blossomServersViewModel.saveFileServers() blossomServersViewModel.saveFileServers()
@@ -182,32 +155,11 @@ fun AllMediaBody(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
contentPadding = FeedPadding, contentPadding = FeedPadding,
) { ) {
item {
SettingsCategory(
stringRes(R.string.media_servers_nip96_section),
stringRes(R.string.media_servers_nip96_explainer),
SettingsCategoryFirstModifier,
)
}
renderMediaServerList(
mediaServersState = nip96ServersState,
keyType = "nip96",
editLabel = R.string.add_a_nip96_server,
emptyLabel = R.string.no_nip96_server_message,
onAddServer = { server ->
nip96ServersViewModel.addServer(server)
},
onDeleteServer = {
nip96ServersViewModel.removeServer(serverUrl = it)
},
)
item { item {
SettingsCategory( SettingsCategory(
stringRes(R.string.media_servers_blossom_section), stringRes(R.string.media_servers_blossom_section),
stringRes(R.string.media_servers_blossom_explainer), stringRes(R.string.media_servers_blossom_explainer),
SettingsCategorySpacingModifier, SettingsCategoryFirstModifier,
) )
} }
@@ -224,6 +176,27 @@ fun AllMediaBody(
}, },
) )
item {
SettingsCategory(
stringRes(R.string.media_servers_nip96_section),
stringRes(R.string.media_servers_nip96_explainer),
SettingsCategorySpacingModifier,
)
}
renderMediaServerList(
mediaServersState = nip96ServersState,
keyType = "nip96",
editLabel = R.string.add_a_nip96_server,
emptyLabel = R.string.no_nip96_server_message,
onAddServer = { server ->
nip96ServersViewModel.addServer(server)
},
onDeleteServer = {
nip96ServersViewModel.removeServer(serverUrl = it)
},
)
DEFAULT_MEDIA_SERVERS.let { DEFAULT_MEDIA_SERVERS.let {
item { item {
SettingsCategoryWithButton( SettingsCategoryWithButton(

View File

@@ -0,0 +1,145 @@
/**
* 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.ui.actions.mediaServers
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.buttons.CloseButton
import com.vitorpamplona.amethyst.ui.note.buttons.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.HalfHorzPadding
import com.vitorpamplona.amethyst.ui.theme.grayText
@Composable
fun AllMediaServersScreen(
accountViewModel: AccountViewModel,
nav: INav,
) {
val nip96ServersViewModel: NIP96ServersViewModel = viewModel()
val blossomServersViewModel: BlossomServersViewModel = viewModel()
LaunchedEffect(key1 = Unit) {
nip96ServersViewModel.load(accountViewModel.account)
blossomServersViewModel.load(accountViewModel.account)
}
MediaServersScaffold(nip96ServersViewModel, blossomServersViewModel) {
nav.popBack()
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MediaServersScaffold(
nip96ServersViewModel: NIP96ServersViewModel,
blossomServersViewModel: BlossomServersViewModel,
onClose: () -> Unit,
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringRes(id = R.string.media_servers),
style = MaterialTheme.typography.titleLarge,
)
}
},
navigationIcon = {
CloseButton(
modifier = HalfHorzPadding,
onPress = {
nip96ServersViewModel.refresh()
blossomServersViewModel.refresh()
onClose()
},
)
},
actions = {
SaveButton(
modifier = HalfHorzPadding,
isActive = true,
onPost = {
nip96ServersViewModel.saveFileServers()
blossomServersViewModel.saveFileServers()
onClose()
},
)
},
colors =
TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
),
)
},
) { padding ->
Column(
modifier =
Modifier
.fillMaxSize()
.padding(
start = 16.dp,
top = padding.calculateTopPadding(),
end = 16.dp,
bottom = padding.calculateBottomPadding(),
).consumeWindowInsets(padding)
.imePadding(),
verticalArrangement = Arrangement.spacedBy(10.dp, alignment = Alignment.Top),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
stringRes(id = R.string.set_preferred_media_servers),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.grayText,
)
AllMediaBody(nip96ServersViewModel, blossomServersViewModel)
}
}
}

View File

@@ -48,6 +48,7 @@ import androidx.navigation.compose.composable
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.relayClient.notifyCommand.compose.DisplayNotifyMessages import com.vitorpamplona.amethyst.service.relayClient.notifyCommand.compose.DisplayNotifyMessages
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataScreen import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataScreen
import com.vitorpamplona.amethyst.ui.actions.mediaServers.AllMediaServersScreen
import com.vitorpamplona.amethyst.ui.components.getActivity import com.vitorpamplona.amethyst.ui.components.getActivity
import com.vitorpamplona.amethyst.ui.components.toasts.DisplayErrorMessages import com.vitorpamplona.amethyst.ui.components.toasts.DisplayErrorMessages
import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen
@@ -131,6 +132,7 @@ fun AppNavigation(
composableFromEnd<Route.UserSettings> { UserSettingsScreen(accountViewModel, nav) } composableFromEnd<Route.UserSettings> { UserSettingsScreen(accountViewModel, nav) }
composableFromBottomArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) } composableFromBottomArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) }
composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) } composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) }
composableFromEndArgs<Route.EditMediaServers> { AllMediaServersScreen(accountViewModel, nav) }
composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) } composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) }
composableFromEndArgs<Route.Profile> { ProfileScreen(it.id, accountViewModel, nav) } composableFromEndArgs<Route.Profile> { ProfileScreen(it.id, accountViewModel, nav) }

View File

@@ -96,7 +96,6 @@ import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserFollowerCount import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserFollowerCount
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
import com.vitorpamplona.amethyst.ui.actions.mediaServers.MediaServersListView
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.note.LoadStatuses import com.vitorpamplona.amethyst.ui.note.LoadStatuses
@@ -422,8 +421,6 @@ fun ListContent(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
var editMediaServers by remember { mutableStateOf(false) }
var backupDialogOpen by remember { mutableStateOf(false) } var backupDialogOpen by remember { mutableStateOf(false) }
Column(modifier) { Column(modifier) {
@@ -465,7 +462,7 @@ fun ListContent(
tint = MaterialTheme.colorScheme.onBackground, tint = MaterialTheme.colorScheme.onBackground,
onClick = { onClick = {
nav.closeDrawer() nav.closeDrawer()
editMediaServers = true nav.nav(Route.EditMediaServers)
}, },
) )
@@ -524,9 +521,6 @@ fun ListContent(
) )
} }
if (editMediaServers) {
MediaServersListView({ editMediaServers = false }, accountViewModel = accountViewModel, nav = nav)
}
if (backupDialogOpen) { if (backupDialogOpen) {
AccountBackupDialog(accountViewModel, onClose = { backupDialogOpen = false }) AccountBackupDialog(accountViewModel, onClose = { backupDialogOpen = false })
} }

View File

@@ -55,6 +55,8 @@ sealed class Route {
@Serializable object EditRelays : Route() @Serializable object EditRelays : Route()
@Serializable object EditMediaServers : Route()
@Serializable data class Nip47NWCSetup( @Serializable data class Nip47NWCSetup(
val nip47: String? = null, val nip47: String? = null,
) : Route() ) : Route()
@@ -221,6 +223,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? {
dest.hasRoute<Route.NewEphemeralChat>() -> entry.toRoute<Route.NewEphemeralChat>() dest.hasRoute<Route.NewEphemeralChat>() -> entry.toRoute<Route.NewEphemeralChat>()
dest.hasRoute<Route.EventRedirect>() -> entry.toRoute<Route.EventRedirect>() dest.hasRoute<Route.EventRedirect>() -> entry.toRoute<Route.EventRedirect>()
dest.hasRoute<Route.EditRelays>() -> entry.toRoute<Route.EditRelays>() dest.hasRoute<Route.EditRelays>() -> entry.toRoute<Route.EditRelays>()
dest.hasRoute<Route.EditMediaServers>() -> entry.toRoute<Route.EditMediaServers>()
dest.hasRoute<Route.Nip47NWCSetup>() -> entry.toRoute<Route.Nip47NWCSetup>() dest.hasRoute<Route.Nip47NWCSetup>() -> entry.toRoute<Route.Nip47NWCSetup>()
dest.hasRoute<Route.Room>() -> entry.toRoute<Route.Room>() dest.hasRoute<Route.Room>() -> entry.toRoute<Route.Room>()
dest.hasRoute<Route.NewPost>() -> entry.toRoute<Route.NewPost>() dest.hasRoute<Route.NewPost>() -> entry.toRoute<Route.NewPost>()

View File

@@ -20,10 +20,8 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -40,7 +38,6 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -74,11 +71,10 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.trusted.TrustedRelay
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.trusted.renderTrustedItems import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.trusted.renderTrustedItems
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.FeedPadding import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.MinHorzSpacer import com.vitorpamplona.amethyst.ui.theme.HalfHorzPadding
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
import com.vitorpamplona.amethyst.ui.theme.SettingsCategoryFirstModifier import com.vitorpamplona.amethyst.ui.theme.SettingsCategoryFirstModifier
import com.vitorpamplona.amethyst.ui.theme.SettingsCategorySpacingModifier import com.vitorpamplona.amethyst.ui.theme.SettingsCategorySpacingModifier
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.grayText import com.vitorpamplona.amethyst.ui.theme.grayText
@Composable @Composable
@@ -86,102 +82,116 @@ fun AllRelayListScreen(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
MappedAllRelayListView(accountViewModel, nav) val dmViewModel: DMRelayListViewModel = viewModel()
val nip65ViewModel: Nip65RelayListViewModel = viewModel()
val privateOutboxViewModel: PrivateOutboxRelayListViewModel = viewModel()
val searchViewModel: SearchRelayListViewModel = viewModel()
val blockedViewModel: BlockedRelayListViewModel = viewModel()
val trustedViewModel: TrustedRelayListViewModel = viewModel()
val localViewModel: LocalRelayListViewModel = viewModel()
val connectedViewModel: ConnectedRelayListViewModel = viewModel()
dmViewModel.init(accountViewModel.account)
nip65ViewModel.init(accountViewModel.account)
searchViewModel.init(accountViewModel.account)
localViewModel.init(accountViewModel.account)
privateOutboxViewModel.init(accountViewModel.account)
connectedViewModel.init(accountViewModel.account)
blockedViewModel.init(accountViewModel.account)
trustedViewModel.init(accountViewModel.account)
LaunchedEffect(accountViewModel.account) {
dmViewModel.load()
nip65ViewModel.load()
searchViewModel.load()
localViewModel.load()
privateOutboxViewModel.load()
connectedViewModel.load()
blockedViewModel.load()
trustedViewModel.load()
}
MappedAllRelayListView(
dmViewModel,
nip65ViewModel,
searchViewModel,
localViewModel,
privateOutboxViewModel,
connectedViewModel,
blockedViewModel,
trustedViewModel,
accountViewModel,
nav,
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MappedAllRelayListView( fun MappedAllRelayListView(
dmViewModel: DMRelayListViewModel,
nip65ViewModel: Nip65RelayListViewModel,
searchViewModel: SearchRelayListViewModel,
localViewModel: LocalRelayListViewModel,
privateOutboxViewModel: PrivateOutboxRelayListViewModel,
connectedViewModel: ConnectedRelayListViewModel,
blockedViewModel: BlockedRelayListViewModel,
trustedViewModel: TrustedRelayListViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
newNav: INav, newNav: INav,
) { ) {
val dmViewModel: DMRelayListViewModel = viewModel()
val dmFeedState by dmViewModel.relays.collectAsStateWithLifecycle() val dmFeedState by dmViewModel.relays.collectAsStateWithLifecycle()
val nip65ViewModel: Nip65RelayListViewModel = viewModel()
val homeFeedState by nip65ViewModel.homeRelays.collectAsStateWithLifecycle() val homeFeedState by nip65ViewModel.homeRelays.collectAsStateWithLifecycle()
val notifFeedState by nip65ViewModel.notificationRelays.collectAsStateWithLifecycle() val notifFeedState by nip65ViewModel.notificationRelays.collectAsStateWithLifecycle()
val privateOutboxViewModel: PrivateOutboxRelayListViewModel = viewModel()
val privateOutboxFeedState by privateOutboxViewModel.relays.collectAsStateWithLifecycle() val privateOutboxFeedState by privateOutboxViewModel.relays.collectAsStateWithLifecycle()
val searchViewModel: SearchRelayListViewModel = viewModel()
val searchFeedState by searchViewModel.relays.collectAsStateWithLifecycle() val searchFeedState by searchViewModel.relays.collectAsStateWithLifecycle()
val blockedViewModel: BlockedRelayListViewModel = viewModel()
val blockedFeedState by blockedViewModel.relays.collectAsStateWithLifecycle() val blockedFeedState by blockedViewModel.relays.collectAsStateWithLifecycle()
val trustedViewModel: TrustedRelayListViewModel = viewModel()
val trustedFeedState by trustedViewModel.relays.collectAsStateWithLifecycle() val trustedFeedState by trustedViewModel.relays.collectAsStateWithLifecycle()
val localViewModel: LocalRelayListViewModel = viewModel()
val localFeedState by localViewModel.relays.collectAsStateWithLifecycle() val localFeedState by localViewModel.relays.collectAsStateWithLifecycle()
val connectedViewModel: ConnectedRelayListViewModel = viewModel()
val connectedRelays by connectedViewModel.relays.collectAsStateWithLifecycle() val connectedRelays by connectedViewModel.relays.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
dmViewModel.load(accountViewModel.account)
nip65ViewModel.load(accountViewModel.account)
searchViewModel.load(accountViewModel.account)
localViewModel.load(accountViewModel.account)
privateOutboxViewModel.load(accountViewModel.account)
connectedViewModel.load(accountViewModel.account)
blockedViewModel.load(accountViewModel.account)
trustedViewModel.load(accountViewModel.account)
}
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { title = {
Row( Text(
text = stringRes(R.string.relay_settings),
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, textAlign = TextAlign.Center,
verticalAlignment = Alignment.CenterVertically, style = MaterialTheme.typography.titleLarge,
) { overflow = TextOverflow.Ellipsis,
Spacer(modifier = MinHorzSpacer) maxLines = 1,
)
Text(
text = stringRes(R.string.relay_settings),
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleLarge,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
SaveButton(
onPost = {
dmViewModel.create()
nip65ViewModel.create()
searchViewModel.create()
localViewModel.create()
privateOutboxViewModel.create()
trustedViewModel.create()
blockedViewModel.create()
newNav.popBack()
},
true,
)
}
}, },
navigationIcon = { navigationIcon = {
Row { CloseButton(
Spacer(modifier = StdHorzSpacer) modifier = HalfHorzPadding,
CloseButton( onPress = {
onPress = { dmViewModel.clear()
dmViewModel.clear() nip65ViewModel.clear()
nip65ViewModel.clear() searchViewModel.clear()
searchViewModel.clear() localViewModel.clear()
localViewModel.clear() privateOutboxViewModel.clear()
privateOutboxViewModel.clear() trustedViewModel.clear()
trustedViewModel.clear() blockedViewModel.clear()
blockedViewModel.clear() newNav.popBack()
newNav.popBack() },
}, )
) },
} actions = {
SaveButton(
modifier = HalfHorzPadding,
isActive = true,
onPost = {
dmViewModel.create()
nip65ViewModel.create()
searchViewModel.create()
localViewModel.create()
privateOutboxViewModel.create()
trustedViewModel.create()
blockedViewModel.create()
newNav.popBack()
},
)
}, },
colors = colors =
TopAppBarDefaults.topAppBarColors( TopAppBarDefaults.topAppBarColors(

View File

@@ -41,8 +41,11 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
var hasModified = false var hasModified = false
fun load(account: Account) { fun init(account: Account) {
this.account = account this.account = account
}
fun load() {
clear() clear()
loadRelayDocuments() loadRelayDocuments()
} }

View File

@@ -64,7 +64,11 @@ fun AddDMRelayListDialog(
) { ) {
val postViewModel: DMRelayListViewModel = viewModel() val postViewModel: DMRelayListViewModel = viewModel()
LaunchedEffect(Unit) { postViewModel.load(accountViewModel.account) } postViewModel.init(accountViewModel.account)
LaunchedEffect(accountViewModel.account) {
postViewModel.load()
}
Dialog( Dialog(
onDismissRequest = onClose, onDismissRequest = onClose,

View File

@@ -49,8 +49,11 @@ class Nip65RelayListViewModel : ViewModel() {
var hasModified = false var hasModified = false
fun load(account: Account) { fun init(account: Account) {
this.account = account this.account = account
}
fun load() {
clear() clear()
loadRelayDocuments() loadRelayDocuments()
} }

View File

@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.search
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -36,8 +35,9 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
@@ -53,7 +53,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInf
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayList import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayList
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayListViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayListViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.HalfHorzPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.imageModifier import com.vitorpamplona.amethyst.ui.theme.imageModifier
@@ -66,7 +66,11 @@ fun AddSearchRelayListDialog(
) { ) {
val postViewModel: SearchRelayListViewModel = viewModel() val postViewModel: SearchRelayListViewModel = viewModel()
LaunchedEffect(Unit) { postViewModel.load(accountViewModel.account) } postViewModel.init(accountViewModel.account)
LaunchedEffect(accountViewModel.account) {
postViewModel.load()
}
Dialog( Dialog(
onDismissRequest = onClose, onDismissRequest = onClose,
@@ -76,34 +80,35 @@ fun AddSearchRelayListDialog(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Spacer(modifier = StdHorzSpacer)
Text(stringRes(R.string.search_relays_title))
SaveButton(
onPost = {
postViewModel.create()
onClose()
},
true,
)
}
},
navigationIcon = { navigationIcon = {
Spacer(modifier = StdHorzSpacer)
CloseButton( CloseButton(
modifier = HalfHorzPadding,
onPress = { onPress = {
postViewModel.clear() postViewModel.clear()
onClose() onClose()
}, },
) )
}, },
title = {
Text(
text = stringRes(R.string.search_relays_title),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleLarge,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
},
actions = {
SaveButton(
modifier = HalfHorzPadding,
isActive = true,
onPost = {
postViewModel.create()
onClose()
},
)
},
colors = colors =
TopAppBarDefaults.topAppBarColors( TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,