mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-27 20:16:37 +02:00
Turns the relay information dialog into a route
This commit is contained in:
@@ -80,6 +80,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScree
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.redirect.LoadRedirectScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.AllRelayListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NIP47SetupScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SecurityFiltersScreen
|
||||
@@ -135,6 +136,7 @@ fun AppNavigation(
|
||||
composableFromEndArgs<Route.Note> { ThreadScreen(it.id, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Hashtag> { HashtagScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Geohash> { GeoHashScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RelayInfo> { RelayInformationScreen(it.url, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Community> { CommunityScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Room> { ChatroomScreen(it.id.toString(), it.message, it.replyId, it.draftId, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RoomByAuthor> { ChatroomByAuthorScreen(it.id, null, accountViewModel, nav) }
|
||||
|
@@ -89,6 +89,10 @@ sealed class Route {
|
||||
val id: String,
|
||||
) : Route()
|
||||
|
||||
@Serializable data class RelayInfo(
|
||||
val url: String,
|
||||
) : Route()
|
||||
|
||||
@Serializable data class EphemeralChat(
|
||||
val id: String,
|
||||
val relayUrl: String,
|
||||
@@ -197,6 +201,8 @@ fun getRouteWithArguments(navController: NavHostController): Route? {
|
||||
dest.hasRoute<Route.Geohash>() -> entry.toRoute<Route.Geohash>()
|
||||
dest.hasRoute<Route.Community>() -> entry.toRoute<Route.Community>()
|
||||
|
||||
dest.hasRoute<Route.RelayInfo>() -> entry.toRoute<Route.RelayInfo>()
|
||||
|
||||
dest.hasRoute<Route.RoomByAuthor>() -> entry.toRoute<Route.RoomByAuthor>()
|
||||
dest.hasRoute<Route.Channel>() -> entry.toRoute<Route.Channel>()
|
||||
dest.hasRoute<Route.ChannelMetadataEdit>() -> entry.toRoute<Route.ChannelMetadataEdit>()
|
||||
|
@@ -53,13 +53,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.header.loadRelayInfo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.LargeRelayIconModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
|
||||
@@ -130,18 +129,6 @@ fun RenderRelay(
|
||||
) {
|
||||
val relayInfo by loadRelayInfo(relay, accountViewModel)
|
||||
|
||||
var openRelayDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (openRelayDialog) {
|
||||
RelayInformationDialog(
|
||||
onClose = { openRelayDialog = false },
|
||||
relayInfo = relayInfo,
|
||||
relay = relay,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val clickableModifier =
|
||||
remember(relay) {
|
||||
@@ -153,33 +140,7 @@ fun RenderRelay(
|
||||
onLongClick = {
|
||||
clipboardManager.setText(AnnotatedString(relay.url))
|
||||
},
|
||||
onClick = {
|
||||
openRelayDialog = true
|
||||
accountViewModel.retrieveRelayDocument(
|
||||
relay,
|
||||
onInfo = { },
|
||||
onError = { url, errorCode, exceptionMessage ->
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.unable_to_download_relay_document,
|
||||
when (errorCode) {
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
|
||||
R.string.relay_information_document_error_failed_to_assemble_url
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER ->
|
||||
R.string.relay_information_document_error_failed_to_reach_server
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT ->
|
||||
R.string.relay_information_document_error_failed_to_parse_response
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS ->
|
||||
R.string.relay_information_document_error_failed_with_http
|
||||
},
|
||||
url.url,
|
||||
exceptionMessage ?: errorCode.toString(),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = { nav.nav(Route.RelayInfo(relay.url)) },
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -52,16 +52,15 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Constants
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.header.loadRelayInfo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
@@ -241,18 +240,6 @@ fun RenderRelayLinePublicChat(
|
||||
@Suppress("ProduceStateDoesNotAssignValue")
|
||||
val relayInfo by loadRelayInfo(relay, accountViewModel)
|
||||
|
||||
var openRelayDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (openRelayDialog) {
|
||||
RelayInformationDialog(
|
||||
onClose = { openRelayDialog = false },
|
||||
relayInfo = relayInfo,
|
||||
relay = relay,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val clickableModifier =
|
||||
remember(relay) {
|
||||
@@ -260,39 +247,13 @@ fun RenderRelayLinePublicChat(
|
||||
onLongClick = {
|
||||
clipboardManager.setText(AnnotatedString(relay.url))
|
||||
},
|
||||
onClick = {
|
||||
openRelayDialog = true
|
||||
accountViewModel.retrieveRelayDocument(
|
||||
relay = relay,
|
||||
onInfo = {},
|
||||
onError = { relay, errorCode, exceptionMessage ->
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.unable_to_download_relay_document,
|
||||
when (errorCode) {
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
|
||||
R.string.relay_information_document_error_failed_to_assemble_url
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER ->
|
||||
R.string.relay_information_document_error_failed_to_reach_server
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT ->
|
||||
R.string.relay_information_document_error_failed_to_parse_response
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS ->
|
||||
R.string.relay_information_document_error_failed_with_http
|
||||
},
|
||||
relay.url,
|
||||
exceptionMessage ?: errorCode.toString(),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = { nav.nav(Route.RelayInfo(relay.url)) },
|
||||
)
|
||||
}
|
||||
|
||||
RenderRelayLine(
|
||||
relay.displayUrl(),
|
||||
relayInfo?.icon,
|
||||
relayInfo.icon,
|
||||
clickableModifier,
|
||||
showPicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
|
@@ -1,439 +0,0 @@
|
||||
/**
|
||||
* 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.screen.loggedIn.relays
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
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
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.timeAgo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.BackButton
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.LargeRelayIconModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayDebugMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayStats
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
||||
import com.vitorpamplona.quartz.nip11RelayInfo.Nip11RelayInformation
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RelayInformationDialog(
|
||||
onClose: () -> Unit,
|
||||
relay: NormalizedRelayUrl,
|
||||
relayInfo: Nip11RelayInformation,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
|
||||
val messages =
|
||||
remember(relay) {
|
||||
RelayStats
|
||||
.get(url = relay)
|
||||
.messages
|
||||
.snapshot()
|
||||
.values
|
||||
.sortedByDescending { it.time }
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = { onClose() },
|
||||
properties =
|
||||
DialogProperties(
|
||||
usePlatformDefaultWidth = false,
|
||||
dismissOnClickOutside = false,
|
||||
),
|
||||
) {
|
||||
SetDialogToEdgeToEdge()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
actions = {},
|
||||
title = {
|
||||
Text(relay.displayUrl())
|
||||
},
|
||||
navigationIcon = {
|
||||
Row {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
BackButton(
|
||||
onPress = { onClose() },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pad ->
|
||||
LazyColumn(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(pad)
|
||||
.consumeWindowInsets(pad)
|
||||
.padding(bottom = Size10dp, start = Size10dp, end = Size10dp)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
item {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = StdPadding.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
RenderRelayIcon(
|
||||
displayUrl = relay.displayUrl(),
|
||||
iconUrl = relayInfo.icon,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
RelayStats.get(relay).pingInMs,
|
||||
iconModifier = LargeRelayIconModifier,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Title(relayInfo.name?.trim() ?: "")
|
||||
Spacer(modifier = HalfVertSpacer)
|
||||
SubtitleContent(relay.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Section(stringRes(R.string.description))
|
||||
|
||||
SectionContent(relayInfo.description?.trim() ?: "")
|
||||
}
|
||||
item {
|
||||
Section(stringRes(R.string.owner))
|
||||
|
||||
relayInfo.pubkey?.let {
|
||||
DisplayOwnerInformation(it, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Section(stringRes(R.string.contact))
|
||||
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
relayInfo.contact?.let {
|
||||
if (it.startsWith("https:")) {
|
||||
ClickableUrl(urlText = it, url = it)
|
||||
} else if (it.startsWith("mailto:") || it.contains('@')) {
|
||||
ClickableEmail(it)
|
||||
} else {
|
||||
SectionContent(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Section(stringRes(R.string.software))
|
||||
|
||||
DisplaySoftwareInformation(relayInfo)
|
||||
|
||||
Section(stringRes(R.string.version))
|
||||
|
||||
SectionContent(relayInfo.version ?: "")
|
||||
}
|
||||
item {
|
||||
Section(stringRes(R.string.supports))
|
||||
|
||||
DisplaySupportedNips(relayInfo)
|
||||
}
|
||||
item {
|
||||
relayInfo.fees?.admission?.let {
|
||||
if (it.isNotEmpty()) {
|
||||
Section(stringRes(R.string.admission_fees))
|
||||
|
||||
it.forEach { item -> SectionContent("${item.amount?.div(1000) ?: 0} sats") }
|
||||
}
|
||||
}
|
||||
|
||||
relayInfo.payments_url?.let {
|
||||
Section(stringRes(R.string.payments_url))
|
||||
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = it,
|
||||
url = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.limitation?.let {
|
||||
Section(stringRes(R.string.limitations))
|
||||
val authRequiredText =
|
||||
if (it.auth_required ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
val paymentRequiredText =
|
||||
if (it.payment_required ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
val restrictedWritesText =
|
||||
if (it.restricted_writes ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
Column {
|
||||
SectionContent(
|
||||
"${stringRes(R.string.message_length)}: ${it.max_message_length ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringRes(R.string.subscriptions)}: ${it.max_subscriptions ?: 0}",
|
||||
)
|
||||
SectionContent("${stringRes(R.string.filters)}: ${it.max_filters ?: 0}")
|
||||
SectionContent(
|
||||
"${stringRes(R.string.subscription_id_length)}: ${it.max_subid_length ?: 0}",
|
||||
)
|
||||
SectionContent("${stringRes(R.string.minimum_prefix)}: ${it.min_prefix ?: 0}")
|
||||
SectionContent(
|
||||
"${stringRes(R.string.maximum_event_tags)}: ${it.max_event_tags ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringRes(R.string.content_length)}: ${it.max_content_length ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringRes(R.string.max_limit)}: ${it.max_limit ?: 0}",
|
||||
)
|
||||
SectionContent("${stringRes(R.string.minimum_pow)}: ${it.min_pow_difficulty ?: 0}")
|
||||
SectionContent("${stringRes(R.string.auth)}: $authRequiredText")
|
||||
SectionContent("${stringRes(R.string.payment)}: $paymentRequiredText")
|
||||
SectionContent("${stringRes(R.string.restricted_writes)}: $restrictedWritesText")
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.relay_countries?.let {
|
||||
Section(stringRes(R.string.countries))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.language_tags?.let {
|
||||
Section(stringRes(R.string.languages))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.tags?.let {
|
||||
Section(stringRes(R.string.tags))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.posting_policy?.let {
|
||||
Section(stringRes(R.string.posting_policy))
|
||||
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
ClickableUrl(
|
||||
it,
|
||||
it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Section(stringRes(R.string.relay_error_messages))
|
||||
}
|
||||
|
||||
items(messages) { msg ->
|
||||
Row {
|
||||
RenderDebugMessage(msg, accountViewModel, newNav)
|
||||
}
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderDebugMessage(
|
||||
msg: RelayDebugMessage,
|
||||
accountViewModel: AccountViewModel,
|
||||
newNav: INav,
|
||||
) {
|
||||
SelectionContainer {
|
||||
val context = LocalContext.current
|
||||
val color =
|
||||
remember {
|
||||
mutableStateOf(Color.Transparent)
|
||||
}
|
||||
TranslatableRichTextViewer(
|
||||
content =
|
||||
remember {
|
||||
"${timeAgo(msg.time, context)}, ${msg.type.name}: ${msg.message}"
|
||||
},
|
||||
canPreview = false,
|
||||
quotesLeft = 0,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
tags = EmptyTagList,
|
||||
backgroundColor = color,
|
||||
id = msg.hashCode().toString(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = newNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
private fun DisplaySupportedNips(relayInfo: Nip11RelayInformation) {
|
||||
FlowRow {
|
||||
relayInfo.supported_nips?.forEach { item ->
|
||||
val text = item.toString().padStart(2, '0')
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = text,
|
||||
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
relayInfo.supported_nip_extensions?.forEach { item ->
|
||||
val text = item.padStart(2, '0')
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = text,
|
||||
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplaySoftwareInformation(relayInfo: Nip11RelayInformation) {
|
||||
val url = (relayInfo.software ?: "").replace("git+", "")
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = url,
|
||||
url = url,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayOwnerInformation(
|
||||
userHex: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadUser(baseUserHex = userHex, accountViewModel) {
|
||||
CrossfadeIfEnabled(it, accountViewModel = accountViewModel) {
|
||||
if (it != null) {
|
||||
UserCompose(
|
||||
baseUser = it,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Title(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SubtitleContent(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Section(text: String) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = text,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp,
|
||||
)
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SectionContent(text: String) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
text = text,
|
||||
)
|
||||
}
|
@@ -0,0 +1,450 @@
|
||||
/**
|
||||
* 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.screen.loggedIn.relays
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
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
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.timeAgo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.header.loadRelayInfo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.BackButton
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.LargeRelayIconModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayDebugMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayStats
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun RelayInformationScreen(
|
||||
relayUrl: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
RelayUrlNormalizer.normalizeOrNull(relayUrl)?.let {
|
||||
RelayInformationScreen(
|
||||
relay = it,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RelayInformationScreen(
|
||||
relay: NormalizedRelayUrl,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
actions = {},
|
||||
title = {
|
||||
Text(relay.displayUrl())
|
||||
},
|
||||
navigationIcon = {
|
||||
Row {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
BackButton(
|
||||
onPress = nav::popBack,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pad ->
|
||||
val relayInfo by loadRelayInfo(relay, accountViewModel)
|
||||
|
||||
val messages =
|
||||
remember(relay) {
|
||||
RelayStats
|
||||
.get(url = relay)
|
||||
.messages
|
||||
.snapshot()
|
||||
.values
|
||||
.sortedByDescending { it.time }
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(pad)
|
||||
.consumeWindowInsets(pad)
|
||||
.padding(bottom = Size10dp, start = Size10dp, end = Size10dp)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
item {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = StdPadding.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
RenderRelayIcon(
|
||||
displayUrl = relay.displayUrl(),
|
||||
iconUrl = relayInfo.icon,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
RelayStats.get(relay).pingInMs,
|
||||
iconModifier = LargeRelayIconModifier,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Title(relayInfo.name?.trim() ?: "")
|
||||
Spacer(modifier = HalfVertSpacer)
|
||||
SubtitleContent(relay.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Section(stringRes(R.string.description))
|
||||
|
||||
SectionContent(relayInfo.description?.trim() ?: stringRes(R.string.no_description))
|
||||
}
|
||||
relayInfo.pubkey?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.owner))
|
||||
DisplayOwnerInformation(it, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
relayInfo.contact?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.contact))
|
||||
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
if (it.startsWith("https:")) {
|
||||
ClickableUrl(urlText = it, url = it)
|
||||
} else if (it.startsWith("mailto:") || it.contains('@')) {
|
||||
ClickableEmail(it)
|
||||
} else {
|
||||
SectionContent(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
relayInfo.software?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.software))
|
||||
|
||||
DisplaySoftwareInformation(it)
|
||||
|
||||
Section(stringRes(R.string.version))
|
||||
|
||||
SectionContent(relayInfo.version ?: "")
|
||||
}
|
||||
}
|
||||
relayInfo.supported_nips?.let {
|
||||
if (it.isNotEmpty()) {
|
||||
item {
|
||||
Section(stringRes(R.string.supports))
|
||||
|
||||
DisplaySupportedNips(it, relayInfo.supported_nip_extensions)
|
||||
}
|
||||
}
|
||||
}
|
||||
relayInfo.fees?.admission?.let {
|
||||
item {
|
||||
if (it.isNotEmpty()) {
|
||||
Section(stringRes(R.string.admission_fees))
|
||||
|
||||
it.forEach { item -> SectionContent("${item.amount?.div(1000) ?: 0} sats") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
relayInfo.payments_url?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.payments_url))
|
||||
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = it,
|
||||
url = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
relayInfo.limitation?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.limitations))
|
||||
val authRequiredText =
|
||||
if (it.auth_required ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
val paymentRequiredText =
|
||||
if (it.payment_required ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
val restrictedWritesText =
|
||||
if (it.restricted_writes ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
Column {
|
||||
SectionContent(
|
||||
"${stringRes(R.string.message_length)}: ${it.max_message_length ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringRes(R.string.subscriptions)}: ${it.max_subscriptions ?: 0}",
|
||||
)
|
||||
SectionContent("${stringRes(R.string.filters)}: ${it.max_filters ?: 0}")
|
||||
SectionContent(
|
||||
"${stringRes(R.string.subscription_id_length)}: ${it.max_subid_length ?: 0}",
|
||||
)
|
||||
SectionContent("${stringRes(R.string.minimum_prefix)}: ${it.min_prefix ?: 0}")
|
||||
SectionContent(
|
||||
"${stringRes(R.string.maximum_event_tags)}: ${it.max_event_tags ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringRes(R.string.content_length)}: ${it.max_content_length ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringRes(R.string.max_limit)}: ${it.max_limit ?: 0}",
|
||||
)
|
||||
SectionContent("${stringRes(R.string.minimum_pow)}: ${it.min_pow_difficulty ?: 0}")
|
||||
SectionContent("${stringRes(R.string.auth)}: $authRequiredText")
|
||||
SectionContent("${stringRes(R.string.payment)}: $paymentRequiredText")
|
||||
SectionContent("${stringRes(R.string.restricted_writes)}: $restrictedWritesText")
|
||||
}
|
||||
}
|
||||
}
|
||||
relayInfo.relay_countries?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.countries))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
relayInfo.language_tags?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.languages))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
relayInfo.tags?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.tags))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
relayInfo.posting_policy?.let {
|
||||
item {
|
||||
Section(stringRes(R.string.posting_policy))
|
||||
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
ClickableUrl(
|
||||
it,
|
||||
it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Section(stringRes(R.string.relay_error_messages))
|
||||
}
|
||||
|
||||
items(messages) { msg ->
|
||||
Row {
|
||||
RenderDebugMessage(msg, accountViewModel, nav)
|
||||
}
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderDebugMessage(
|
||||
msg: RelayDebugMessage,
|
||||
accountViewModel: AccountViewModel,
|
||||
newNav: INav,
|
||||
) {
|
||||
SelectionContainer {
|
||||
val context = LocalContext.current
|
||||
val color =
|
||||
remember {
|
||||
mutableStateOf(Color.Transparent)
|
||||
}
|
||||
TranslatableRichTextViewer(
|
||||
content =
|
||||
remember {
|
||||
"${timeAgo(msg.time, context)}, ${msg.type.name}: ${msg.message}"
|
||||
},
|
||||
canPreview = false,
|
||||
quotesLeft = 0,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
tags = EmptyTagList,
|
||||
backgroundColor = color,
|
||||
id = msg.hashCode().toString(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = newNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
private fun DisplaySupportedNips(
|
||||
supportedNips: List<Int>,
|
||||
supportedNipExtensions: List<String>?,
|
||||
) {
|
||||
FlowRow {
|
||||
supportedNips.forEach { item ->
|
||||
val text = item.toString().padStart(2, '0')
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = text,
|
||||
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
supportedNipExtensions?.forEach { item ->
|
||||
val text = item.padStart(2, '0')
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = text,
|
||||
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplaySoftwareInformation(software: String) {
|
||||
val url = software.replace("git+", "")
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
ClickableUrl(
|
||||
urlText = url,
|
||||
url = url,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayOwnerInformation(
|
||||
userHex: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadUser(baseUserHex = userHex, accountViewModel) {
|
||||
CrossfadeIfEnabled(it, accountViewModel = accountViewModel) {
|
||||
if (it != null) {
|
||||
UserCompose(
|
||||
baseUser = it,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Title(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SubtitleContent(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Section(text: String) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = text,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp,
|
||||
)
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SectionContent(text: String) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
text = text,
|
||||
)
|
||||
}
|
@@ -22,19 +22,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav.nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.header.loadRelayInfo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
@Composable
|
||||
fun BasicRelaySetupInfoDialog(
|
||||
@@ -43,75 +35,14 @@ fun BasicRelaySetupInfoDialog(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val relayInfo by loadRelayInfo(item.relay, accountViewModel)
|
||||
|
||||
var openRelayDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (openRelayDialog) {
|
||||
RelayInformationDialog(
|
||||
onClose = { openRelayDialog = false },
|
||||
relayInfo = relayInfo,
|
||||
relay = item.relay,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
BasicRelaySetupInfoClickableRow(
|
||||
item = item,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
onDelete = onDelete,
|
||||
accountViewModel = accountViewModel,
|
||||
onClick = {
|
||||
openRelayDialog = true
|
||||
accountViewModel.retrieveRelayDocument(
|
||||
relay = item.relay,
|
||||
onInfo = { },
|
||||
onError = { relay, errorCode, exceptionMessage ->
|
||||
val msg =
|
||||
when (errorCode) {
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
relay.url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER ->
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
relay.url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT ->
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
relay.url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS ->
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
relay.url,
|
||||
exceptionMessage,
|
||||
)
|
||||
}
|
||||
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = { nav.nav(Route.RelayInfo(item.relay.url)) },
|
||||
)
|
||||
}
|
||||
|
@@ -107,6 +107,7 @@
|
||||
<string name="my_awesome_group">My Awesome Group</string>
|
||||
<string name="picture_url">Picture Url</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="no_description">Description not found</string>
|
||||
<string name="about_us">"About us.. "</string>
|
||||
<string name="what_s_on_your_mind">What\'s on your mind?</string>
|
||||
<string name="write_a_message">Write a message…</string>
|
||||
|
Reference in New Issue
Block a user