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.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<NoteState> = getBlossomServersNote().flow().metadata.stateFlow
fun getBlossomServersList(): BlossomServersEvent? = getBlossomServersNote().event as? BlossomServersEvent
fun normalizeServers(note: Note): List<String> {
val event = note.event as? FileServersEvent
val event = note.event as? BlossomServersEvent
return event?.servers() ?: emptyList()
}

View File

@@ -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(

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.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<Route.UserSettings> { UserSettingsScreen(accountViewModel, nav) }
composableFromBottomArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) }
composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) }
composableFromEndArgs<Route.EditMediaServers> { AllMediaServersScreen(accountViewModel, nav) }
composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(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.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 })
}

View File

@@ -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<Route.NewEphemeralChat>() -> entry.toRoute<Route.NewEphemeralChat>()
dest.hasRoute<Route.EventRedirect>() -> entry.toRoute<Route.EventRedirect>()
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.Room>() -> entry.toRoute<Route.Room>()
dest.hasRoute<Route.NewPost>() -> entry.toRoute<Route.NewPost>()

View File

@@ -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(

View File

@@ -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()
}

View File

@@ -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,

View File

@@ -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()
}

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.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,