From e37d6ebd8b97f218c141e54194ad82af21bfca65 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 11 Jul 2025 09:22:27 -0400 Subject: [PATCH] Moves media server from Dialog to Screen with full route. --- .../nipB7Blossom/BlossomServerListState.kt | 5 +- .../mediaServers/AllMediaServersLIstView.kt | 79 +++------ .../mediaServers/AllMediaServersScreen.kt | 145 ++++++++++++++++ .../amethyst/ui/navigation/AppNavigation.kt | 2 + .../amethyst/ui/navigation/DrawerContent.kt | 8 +- .../amethyst/ui/navigation/Routes.kt | 3 + .../loggedIn/relays/AllRelayListScreen.kt | 162 ++++++++++-------- .../relays/common/BasicRelaySetupInfoModel.kt | 5 +- .../relays/dm/AddDMRelayListDialog.kt | 6 +- .../relays/nip65/Nip65RelayListViewModel.kt | 5 +- .../search/AddSearchRelayListDialog.kt | 53 +++--- 11 files changed, 307 insertions(+), 166 deletions(-) create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersScreen.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt index 69c910da8..927a730c7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt @@ -27,7 +27,6 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.NoteState import com.vitorpamplona.quartz.nip01Core.core.HexKey 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.BlossomServersEvent import com.vitorpamplona.quartz.utils.tryAndWait @@ -49,14 +48,14 @@ class BlossomServerListState( ) { fun getBlossomServersAddress() = BlossomServersEvent.createAddress(signer.pubKey) - fun getBlossomServersNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(getBlossomServersAddress()) + fun getBlossomServersNote(): AddressableNote = cache.getOrCreateAddressableNote(getBlossomServersAddress()) fun getBlossomServersListFlow(): StateFlow = getBlossomServersNote().flow().metadata.stateFlow fun getBlossomServersList(): BlossomServersEvent? = getBlossomServersNote().event as? BlossomServersEvent fun normalizeServers(note: Note): List { - val event = note.event as? FileServersEvent + val event = note.event as? BlossomServersEvent return event?.servers() ?: emptyList() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt index 0beb60086..f89bb83ab 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt @@ -45,59 +45,30 @@ 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.runtime.getValue 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.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel 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.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.SettingsCategoryWithButton import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleVertPadding 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.SettingsCategorySpacingModifier import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer 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) @Composable -fun DialogContent( +fun MediaServersListView( nip96ServersViewModel: NIP96ServersViewModel, blossomServersViewModel: BlossomServersViewModel, onClose: () -> Unit, @@ -119,6 +90,7 @@ fun DialogContent( }, navigationIcon = { CloseButton( + modifier = HalfHorzPadding, onPress = { nip96ServersViewModel.refresh() blossomServersViewModel.refresh() @@ -128,6 +100,7 @@ fun DialogContent( }, actions = { SaveButton( + modifier = HalfHorzPadding, onPost = { nip96ServersViewModel.saveFileServers() blossomServersViewModel.saveFileServers() @@ -182,32 +155,11 @@ fun AllMediaBody( horizontalAlignment = Alignment.CenterHorizontally, 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 { SettingsCategory( stringRes(R.string.media_servers_blossom_section), 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 { item { SettingsCategoryWithButton( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersScreen.kt new file mode 100644 index 000000000..17be2ff5b --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersScreen.kt @@ -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) + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index 863975fec..297044909 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -48,6 +48,7 @@ import androidx.navigation.compose.composable import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.service.relayClient.notifyCommand.compose.DisplayNotifyMessages 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.toasts.DisplayErrorMessages import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen @@ -131,6 +132,7 @@ fun AppNavigation( composableFromEnd { UserSettingsScreen(accountViewModel, nav) } composableFromBottomArgs { NIP47SetupScreen(accountViewModel, nav, it.nip47) } composableFromEndArgs { AllRelayListScreen(accountViewModel, nav) } + composableFromEndArgs { AllMediaServersScreen(accountViewModel, nav) } composableFromEndArgs { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) } composableFromEndArgs { ProfileScreen(it.id, accountViewModel, nav) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt index 06ba28a75..37026ed36 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt @@ -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.user.observeUserFollowerCount 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.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.LoadStatuses @@ -422,8 +421,6 @@ fun ListContent( accountViewModel: AccountViewModel, nav: INav, ) { - var editMediaServers by remember { mutableStateOf(false) } - var backupDialogOpen by remember { mutableStateOf(false) } Column(modifier) { @@ -465,7 +462,7 @@ fun ListContent( tint = MaterialTheme.colorScheme.onBackground, onClick = { 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) { AccountBackupDialog(accountViewModel, onClose = { backupDialogOpen = false }) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt index bbabd8561..4c7a06bb8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt @@ -55,6 +55,8 @@ sealed class Route { @Serializable object EditRelays : Route() + @Serializable object EditMediaServers : Route() + @Serializable data class Nip47NWCSetup( val nip47: String? = null, ) : Route() @@ -221,6 +223,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? { dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() + dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt index d23aa6479..4afe17001 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt @@ -20,10 +20,8 @@ */ 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.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -40,7 +38,6 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign 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.stringRes 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.SettingsCategoryFirstModifier import com.vitorpamplona.amethyst.ui.theme.SettingsCategorySpacingModifier -import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.grayText @Composable @@ -86,102 +82,116 @@ fun AllRelayListScreen( accountViewModel: AccountViewModel, 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) @Composable fun MappedAllRelayListView( + dmViewModel: DMRelayListViewModel, + nip65ViewModel: Nip65RelayListViewModel, + searchViewModel: SearchRelayListViewModel, + localViewModel: LocalRelayListViewModel, + privateOutboxViewModel: PrivateOutboxRelayListViewModel, + connectedViewModel: ConnectedRelayListViewModel, + blockedViewModel: BlockedRelayListViewModel, + trustedViewModel: TrustedRelayListViewModel, accountViewModel: AccountViewModel, newNav: INav, ) { - val dmViewModel: DMRelayListViewModel = viewModel() val dmFeedState by dmViewModel.relays.collectAsStateWithLifecycle() - - val nip65ViewModel: Nip65RelayListViewModel = viewModel() val homeFeedState by nip65ViewModel.homeRelays.collectAsStateWithLifecycle() val notifFeedState by nip65ViewModel.notificationRelays.collectAsStateWithLifecycle() - - val privateOutboxViewModel: PrivateOutboxRelayListViewModel = viewModel() val privateOutboxFeedState by privateOutboxViewModel.relays.collectAsStateWithLifecycle() - - val searchViewModel: SearchRelayListViewModel = viewModel() val searchFeedState by searchViewModel.relays.collectAsStateWithLifecycle() - - val blockedViewModel: BlockedRelayListViewModel = viewModel() val blockedFeedState by blockedViewModel.relays.collectAsStateWithLifecycle() - - val trustedViewModel: TrustedRelayListViewModel = viewModel() val trustedFeedState by trustedViewModel.relays.collectAsStateWithLifecycle() - - val localViewModel: LocalRelayListViewModel = viewModel() val localFeedState by localViewModel.relays.collectAsStateWithLifecycle() - - val connectedViewModel: ConnectedRelayListViewModel = viewModel() 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( topBar = { TopAppBar( title = { - Row( + Text( + text = stringRes(R.string.relay_settings), modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Spacer(modifier = MinHorzSpacer) - - 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, - ) - } + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleLarge, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) }, navigationIcon = { - Row { - Spacer(modifier = StdHorzSpacer) - CloseButton( - onPress = { - dmViewModel.clear() - nip65ViewModel.clear() - searchViewModel.clear() - localViewModel.clear() - privateOutboxViewModel.clear() - trustedViewModel.clear() - blockedViewModel.clear() - newNav.popBack() - }, - ) - } + CloseButton( + modifier = HalfHorzPadding, + onPress = { + dmViewModel.clear() + nip65ViewModel.clear() + searchViewModel.clear() + localViewModel.clear() + privateOutboxViewModel.clear() + trustedViewModel.clear() + blockedViewModel.clear() + 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 = TopAppBarDefaults.topAppBarColors( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt index 5ae211596..37151d9c7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt @@ -41,8 +41,11 @@ abstract class BasicRelaySetupInfoModel : ViewModel() { var hasModified = false - fun load(account: Account) { + fun init(account: Account) { this.account = account + } + + fun load() { clear() loadRelayDocuments() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/AddDMRelayListDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/AddDMRelayListDialog.kt index 0915c4d97..74240d809 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/AddDMRelayListDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/AddDMRelayListDialog.kt @@ -64,7 +64,11 @@ fun AddDMRelayListDialog( ) { val postViewModel: DMRelayListViewModel = viewModel() - LaunchedEffect(Unit) { postViewModel.load(accountViewModel.account) } + postViewModel.init(accountViewModel.account) + + LaunchedEffect(accountViewModel.account) { + postViewModel.load() + } Dialog( onDismissRequest = onClose, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListViewModel.kt index fee19cdde..2b5d12620 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListViewModel.kt @@ -49,8 +49,11 @@ class Nip65RelayListViewModel : ViewModel() { var hasModified = false - fun load(account: Account) { + fun init(account: Account) { this.account = account + } + + fun load() { clear() loadRelayDocuments() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/AddSearchRelayListDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/AddSearchRelayListDialog.kt index 2648cc4f7..a5a0cea80 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/AddSearchRelayListDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/AddSearchRelayListDialog.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.search import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -36,8 +35,9 @@ 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.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog 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.SearchRelayListViewModel 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.imageModifier @@ -66,7 +66,11 @@ fun AddSearchRelayListDialog( ) { val postViewModel: SearchRelayListViewModel = viewModel() - LaunchedEffect(Unit) { postViewModel.load(accountViewModel.account) } + postViewModel.init(accountViewModel.account) + + LaunchedEffect(accountViewModel.account) { + postViewModel.load() + } Dialog( onDismissRequest = onClose, @@ -76,34 +80,35 @@ fun AddSearchRelayListDialog( Scaffold( topBar = { 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 = { - Spacer(modifier = StdHorzSpacer) CloseButton( + modifier = HalfHorzPadding, onPress = { postViewModel.clear() 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 = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.surface,