Improvements to the relay settings page.
This commit is contained in:
@@ -14,4 +14,6 @@ data class RelaySetupInfo(
|
|||||||
val spamCount: Int = 0,
|
val spamCount: Int = 0,
|
||||||
val feedTypes: Set<FeedType>,
|
val feedTypes: Set<FeedType>,
|
||||||
val paidRelay: Boolean = false
|
val paidRelay: Boolean = false
|
||||||
)
|
) {
|
||||||
|
val briefInfo: RelayBriefInfo = RelayBriefInfo(url)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.vitorpamplona.amethyst.service
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.LruCache
|
||||||
|
import com.vitorpamplona.amethyst.model.RelayInformation
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
object Nip11CachedRetriever {
|
||||||
|
val relayInformationDocumentCache = LruCache<String, RelayInformation>(100)
|
||||||
|
val retriever = Nip11Retriever()
|
||||||
|
|
||||||
|
suspend fun loadRelayInfo(
|
||||||
|
dirtyUrl: String,
|
||||||
|
onInfo: (RelayInformation) -> Unit,
|
||||||
|
onError: (String, Nip11Retriever.ErrorCode, String?) -> Unit
|
||||||
|
) {
|
||||||
|
val url = retriever.cleanUrl(dirtyUrl)
|
||||||
|
val doc = relayInformationDocumentCache.get(url)
|
||||||
|
|
||||||
|
if (doc != null) {
|
||||||
|
onInfo(doc)
|
||||||
|
} else {
|
||||||
|
Nip11Retriever().loadRelayInfo(
|
||||||
|
url,
|
||||||
|
dirtyUrl,
|
||||||
|
onInfo = {
|
||||||
|
relayInformationDocumentCache.put(url, it)
|
||||||
|
onInfo(it)
|
||||||
|
},
|
||||||
|
onError
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nip11Retriever {
|
||||||
|
enum class ErrorCode {
|
||||||
|
FAIL_TO_ASSEMBLE_URL,
|
||||||
|
FAIL_TO_REACH_SERVER,
|
||||||
|
FAIL_TO_PARSE_RESULT,
|
||||||
|
FAIL_WITH_HTTP_STATUS
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanUrl(dirtyUrl: String): String {
|
||||||
|
return if (dirtyUrl.contains("://")) {
|
||||||
|
dirtyUrl
|
||||||
|
.replace("wss://", "https://")
|
||||||
|
.replace("ws://", "http://")
|
||||||
|
} else {
|
||||||
|
"https://$dirtyUrl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadRelayInfo(
|
||||||
|
url: String,
|
||||||
|
dirtyUrl: String,
|
||||||
|
onInfo: (RelayInformation) -> Unit,
|
||||||
|
onError: (String, ErrorCode, String?) -> Unit
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
val request: Request = Request
|
||||||
|
.Builder()
|
||||||
|
.header("Accept", "application/nostr+json")
|
||||||
|
.url(url)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
HttpClient.getHttpClient()
|
||||||
|
.newCall(request)
|
||||||
|
.enqueue(
|
||||||
|
object : Callback {
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
checkNotInMainThread()
|
||||||
|
response.use {
|
||||||
|
val body = it.body.string()
|
||||||
|
try {
|
||||||
|
if (it.isSuccessful) {
|
||||||
|
onInfo(RelayInformation.fromJson(body))
|
||||||
|
} else {
|
||||||
|
onError(dirtyUrl, ErrorCode.FAIL_WITH_HTTP_STATUS, it.code.toString())
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("RelayInfoFail", "Resulting Message from Relay $dirtyUrl in not parseable: $body", e)
|
||||||
|
onError(dirtyUrl, ErrorCode.FAIL_TO_PARSE_RESULT, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
Log.e("RelayInfoFail", "$dirtyUrl unavailable", e)
|
||||||
|
onError(dirtyUrl, ErrorCode.FAIL_TO_REACH_SERVER, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("RelayInfoFail", "Invalid URL $dirtyUrl", e)
|
||||||
|
onError(dirtyUrl, ErrorCode.FAIL_TO_ASSEMBLE_URL, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.actions
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -50,24 +49,35 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.RelayInformation
|
import com.vitorpamplona.amethyst.model.RelayBriefInfo
|
||||||
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
||||||
|
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||||
|
import com.vitorpamplona.amethyst.service.relays.Constants
|
||||||
import com.vitorpamplona.amethyst.service.relays.Constants.defaultRelays
|
import com.vitorpamplona.amethyst.service.relays.Constants.defaultRelays
|
||||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.HalfHorzPadding
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.ReactionRowHeightChat
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.WarningColor
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.allGoodColor
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import com.vitorpamplona.amethyst.ui.theme.warningColor
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.lang.Math.round
|
import java.lang.Math.round
|
||||||
|
|
||||||
@@ -75,16 +85,9 @@ import java.lang.Math.round
|
|||||||
fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, relayToAdd: String = "", nav: (String) -> Unit) {
|
fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, relayToAdd: String = "", nav: (String) -> Unit) {
|
||||||
val postViewModel: NewRelayListViewModel = viewModel()
|
val postViewModel: NewRelayListViewModel = viewModel()
|
||||||
val feedState by postViewModel.relays.collectAsState()
|
val feedState by postViewModel.relays.collectAsState()
|
||||||
val context = LocalContext.current
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
postViewModel.load(accountViewModel.account)
|
postViewModel.load(accountViewModel.account)
|
||||||
postViewModel.relays.value.forEach { item ->
|
|
||||||
loadRelayInfo(item.url, context, scope) {
|
|
||||||
postViewModel.togglePaidRelay(item, it.limitation?.payment_required ?: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
@@ -96,7 +99,9 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
|
|||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth().padding(end = 10.dp),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(end = 10.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -108,11 +113,7 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
|
|||||||
defaultRelays.forEach {
|
defaultRelays.forEach {
|
||||||
postViewModel.addRelay(it)
|
postViewModel.addRelay(it)
|
||||||
}
|
}
|
||||||
postViewModel.relays.value.forEach { item ->
|
postViewModel.loadRelayDocuments()
|
||||||
loadRelayInfo(item.url, context, scope) {
|
|
||||||
postViewModel.togglePaidRelay(item, it.limitation?.payment_required ?: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.default_relays))
|
Text(stringResource(R.string.default_relays))
|
||||||
@@ -139,8 +140,6 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { pad ->
|
) { pad ->
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier.padding(
|
||||||
16.dp,
|
16.dp,
|
||||||
@@ -158,10 +157,6 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
itemsIndexed(feedState, key = { _, item -> item.url }) { index, item ->
|
itemsIndexed(feedState, key = { _, item -> item.url }) { index, item ->
|
||||||
if (index == 0) {
|
|
||||||
ServerConfigHeader()
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerConfig(
|
ServerConfig(
|
||||||
item,
|
item,
|
||||||
onToggleDownload = { postViewModel.toggleDownload(it) },
|
onToggleDownload = { postViewModel.toggleDownload(it) },
|
||||||
@@ -175,9 +170,7 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
|
|||||||
|
|
||||||
onDelete = { postViewModel.deleteRelay(it) },
|
onDelete = { postViewModel.deleteRelay(it) },
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav
|
||||||
scope = scope,
|
|
||||||
context = context
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,13 +254,38 @@ fun ServerConfigHeader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun ServerConfigPreview() {
|
||||||
|
ServerConfigClickableLine(
|
||||||
|
item = RelaySetupInfo(
|
||||||
|
url = "nostr.mom",
|
||||||
|
read = true,
|
||||||
|
write = true,
|
||||||
|
errorCount = 23,
|
||||||
|
downloadCountInBytes = 10000,
|
||||||
|
uploadCountInBytes = 10000000,
|
||||||
|
spamCount = 10,
|
||||||
|
feedTypes = Constants.activeTypesGlobalChats,
|
||||||
|
paidRelay = true
|
||||||
|
),
|
||||||
|
onDelete = {},
|
||||||
|
onToggleDownload = {},
|
||||||
|
onToggleUpload = {},
|
||||||
|
onToggleFollows = {},
|
||||||
|
onTogglePrivateDMs = {},
|
||||||
|
onTogglePublicChats = {},
|
||||||
|
onToggleGlobal = {},
|
||||||
|
onToggleSearch = {},
|
||||||
|
onClick = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ServerConfig(
|
fun ServerConfig(
|
||||||
item: RelaySetupInfo,
|
item: RelaySetupInfo,
|
||||||
onToggleDownload: (RelaySetupInfo) -> Unit,
|
onToggleDownload: (RelaySetupInfo) -> Unit,
|
||||||
onToggleUpload: (RelaySetupInfo) -> Unit,
|
onToggleUpload: (RelaySetupInfo) -> Unit,
|
||||||
|
|
||||||
onToggleFollows: (RelaySetupInfo) -> Unit,
|
onToggleFollows: (RelaySetupInfo) -> Unit,
|
||||||
onTogglePrivateDMs: (RelaySetupInfo) -> Unit,
|
onTogglePrivateDMs: (RelaySetupInfo) -> Unit,
|
||||||
onTogglePublicChats: (RelaySetupInfo) -> Unit,
|
onTogglePublicChats: (RelaySetupInfo) -> Unit,
|
||||||
@@ -276,84 +294,297 @@ fun ServerConfig(
|
|||||||
|
|
||||||
onDelete: (RelaySetupInfo) -> Unit,
|
onDelete: (RelaySetupInfo) -> Unit,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit
|
||||||
context: Context,
|
|
||||||
scope: CoroutineScope
|
|
||||||
) {
|
) {
|
||||||
var relayInfo: RelayInformation? by remember { mutableStateOf(null) }
|
var relayInfo: RelayInfoDialog? by remember { mutableStateOf(null) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
if (relayInfo != null) {
|
relayInfo?.let {
|
||||||
RelayInformationDialog(
|
RelayInformationDialog(
|
||||||
onClose = {
|
onClose = { relayInfo = null },
|
||||||
relayInfo = null
|
relayInfo = it.relayInfo,
|
||||||
},
|
relayBriefInfo = it.relayBriefInfo,
|
||||||
relayInfo = relayInfo!!,
|
accountViewModel = accountViewModel,
|
||||||
accountViewModel,
|
nav = nav
|
||||||
nav
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerConfigClickableLine(
|
||||||
|
item = item,
|
||||||
|
onToggleDownload = onToggleDownload,
|
||||||
|
onToggleUpload = onToggleUpload,
|
||||||
|
onToggleFollows = onToggleFollows,
|
||||||
|
onTogglePrivateDMs = onTogglePrivateDMs,
|
||||||
|
onTogglePublicChats = onTogglePublicChats,
|
||||||
|
onToggleGlobal = onToggleGlobal,
|
||||||
|
onToggleSearch = onToggleSearch,
|
||||||
|
onDelete = onDelete,
|
||||||
|
onClick = {
|
||||||
|
accountViewModel.retrieveRelayDocument(
|
||||||
|
item.url,
|
||||||
|
onInfo = {
|
||||||
|
relayInfo = RelayInfoDialog(RelayBriefInfo(item.url), it)
|
||||||
|
},
|
||||||
|
onError = { url, errorCode, exceptionMessage ->
|
||||||
|
val msg = when (errorCode) {
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
msg,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ServerConfigClickableLine(
|
||||||
|
item: RelaySetupInfo,
|
||||||
|
onToggleDownload: (RelaySetupInfo) -> Unit,
|
||||||
|
onToggleUpload: (RelaySetupInfo) -> Unit,
|
||||||
|
onToggleFollows: (RelaySetupInfo) -> Unit,
|
||||||
|
onTogglePrivateDMs: (RelaySetupInfo) -> Unit,
|
||||||
|
onTogglePublicChats: (RelaySetupInfo) -> Unit,
|
||||||
|
onToggleGlobal: (RelaySetupInfo) -> Unit,
|
||||||
|
onToggleSearch: (RelaySetupInfo) -> Unit,
|
||||||
|
onDelete: (RelaySetupInfo) -> Unit,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(vertical = 5.dp)
|
modifier = Modifier.padding(vertical = 5.dp)
|
||||||
) {
|
) {
|
||||||
Column() {
|
Column(Modifier.clickable(onClick = onClick)) {
|
||||||
IconButton(
|
RenderRelayIcon(iconUrl = item.briefInfo.favIcon, Size55dp)
|
||||||
modifier = Modifier.size(30.dp),
|
}
|
||||||
onClick = { onDelete(item) }
|
|
||||||
|
Spacer(modifier = HalfHorzPadding)
|
||||||
|
|
||||||
|
Column(Modifier.weight(1f)) {
|
||||||
|
FirstLine(item, onClick, onDelete, ReactionRowHeightChat.fillMaxWidth())
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = ReactionRowHeightChat.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Icon(
|
RenderActiveToggles(
|
||||||
imageVector = Icons.Default.Cancel,
|
item = item,
|
||||||
null,
|
onToggleFollows = onToggleFollows,
|
||||||
modifier = Modifier
|
onTogglePrivateDMs = onTogglePrivateDMs,
|
||||||
.padding(end = 5.dp)
|
onTogglePublicChats = onTogglePublicChats,
|
||||||
.size(15.dp),
|
onToggleGlobal = onToggleGlobal,
|
||||||
tint = Color.Red
|
onToggleSearch = onToggleSearch
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = ReactionRowHeightChat.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
RenderStatusRow(
|
||||||
|
item = item,
|
||||||
|
onToggleDownload = onToggleDownload,
|
||||||
|
onToggleUpload = onToggleUpload,
|
||||||
|
modifier = HalfStartPadding.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(Modifier.weight(1f)) {
|
Divider(
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
thickness = 0.25.dp
|
||||||
if (item.paidRelay) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Paid,
|
|
||||||
null,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(end = 5.dp)
|
|
||||||
.size(14.dp),
|
|
||||||
tint = Color.Green
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
private fun RenderStatusRow(
|
||||||
|
item: RelaySetupInfo,
|
||||||
|
onToggleDownload: (RelaySetupInfo) -> Unit,
|
||||||
|
onToggleUpload: (RelaySetupInfo) -> Unit,
|
||||||
|
modifier: Modifier
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Download,
|
||||||
|
contentDescription = stringResource(R.string.read_from_relay),
|
||||||
|
modifier = Modifier
|
||||||
|
.size(15.dp)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = { onToggleDownload(item) },
|
||||||
|
onLongClick = {
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.read_from_relay),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
tint = if (item.read) {
|
||||||
|
MaterialTheme.colors.allGoodColor
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colors.onSurface.copy(
|
||||||
|
alpha = 0.32f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = item.url.removePrefix("wss://").removeSuffix("/"),
|
text = countToHumanReadableBytes(item.downloadCountInBytes),
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.clickable {
|
|
||||||
loadRelayInfo(item.url, context, scope) {
|
|
||||||
relayInfo = it
|
|
||||||
}
|
|
||||||
},
|
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
fontSize = 12.sp,
|
||||||
|
modifier = modifier,
|
||||||
|
color = MaterialTheme.colors.placeholderText
|
||||||
|
)
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Upload,
|
||||||
|
stringResource(R.string.write_to_relay),
|
||||||
|
modifier = Modifier
|
||||||
|
.size(15.dp)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = { onToggleUpload(item) },
|
||||||
|
onLongClick = {
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.write_to_relay),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
tint = if (item.write) {
|
||||||
|
MaterialTheme.colors.allGoodColor
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colors.onSurface.copy(
|
||||||
|
alpha = 0.32f
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Text(
|
||||||
Column(Modifier.weight(1f)) {
|
text = countToHumanReadableBytes(item.uploadCountInBytes),
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
maxLines = 1,
|
||||||
IconButton(
|
fontSize = 12.sp,
|
||||||
|
modifier = modifier,
|
||||||
|
color = MaterialTheme.colors.placeholderText
|
||||||
|
)
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.SyncProblem,
|
||||||
|
stringResource(R.string.errors),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(30.dp),
|
.size(15.dp)
|
||||||
onClick = { }
|
.combinedClickable(
|
||||||
|
onClick = { },
|
||||||
|
onLongClick = {
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.errors),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
tint = if (item.errorCount > 0) MaterialTheme.colors.warningColor else MaterialTheme.colors.allGoodColor
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = countToHumanReadable(item.errorCount, "errors"),
|
||||||
|
maxLines = 1,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
modifier = modifier,
|
||||||
|
color = MaterialTheme.colors.placeholderText
|
||||||
|
)
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.DeleteSweep,
|
||||||
|
stringResource(R.string.spam),
|
||||||
|
modifier = Modifier
|
||||||
|
.size(15.dp)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = { },
|
||||||
|
onLongClick = {
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.spam),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
tint = if (item.spamCount > 0) MaterialTheme.colors.warningColor else MaterialTheme.colors.allGoodColor
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = countToHumanReadable(item.spamCount, "spam"),
|
||||||
|
maxLines = 1,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
modifier = modifier,
|
||||||
|
color = MaterialTheme.colors.placeholderText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
private fun RenderActiveToggles(
|
||||||
|
item: RelaySetupInfo,
|
||||||
|
onToggleFollows: (RelaySetupInfo) -> Unit,
|
||||||
|
onTogglePrivateDMs: (RelaySetupInfo) -> Unit,
|
||||||
|
onTogglePublicChats: (RelaySetupInfo) -> Unit,
|
||||||
|
onToggleGlobal: (RelaySetupInfo) -> Unit,
|
||||||
|
onToggleSearch: (RelaySetupInfo) -> Unit
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.active_for),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
color = MaterialTheme.colors.placeholderText,
|
||||||
|
modifier = Modifier.padding(start = 2.dp, end = 5.dp),
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
modifier = Size30Modifier,
|
||||||
|
onClick = { onToggleFollows(item) }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painterResource(R.drawable.ic_home),
|
painterResource(R.drawable.ic_home),
|
||||||
stringResource(R.string.home_feed),
|
stringResource(R.string.home_feed),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = 5.dp)
|
.padding(horizontal = 5.dp)
|
||||||
.size(15.dp)
|
.size(15.dp)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = { onToggleFollows(item) },
|
onClick = { onToggleFollows(item) },
|
||||||
@@ -370,7 +601,7 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
tint = if (item.feedTypes.contains(FeedType.FOLLOWS)) {
|
tint = if (item.feedTypes.contains(FeedType.FOLLOWS)) {
|
||||||
Color.Green
|
MaterialTheme.colors.allGoodColor
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colors.onSurface.copy(
|
MaterialTheme.colors.onSurface.copy(
|
||||||
alpha = 0.32f
|
alpha = 0.32f
|
||||||
@@ -379,8 +610,8 @@ fun ServerConfig(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(30.dp),
|
modifier = Size30Modifier,
|
||||||
onClick = { }
|
onClick = { onTogglePrivateDMs(item) }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painterResource(R.drawable.ic_dm),
|
painterResource(R.drawable.ic_dm),
|
||||||
@@ -403,7 +634,7 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
tint = if (item.feedTypes.contains(FeedType.PRIVATE_DMS)) {
|
tint = if (item.feedTypes.contains(FeedType.PRIVATE_DMS)) {
|
||||||
Color.Green
|
MaterialTheme.colors.allGoodColor
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colors.onSurface.copy(
|
MaterialTheme.colors.onSurface.copy(
|
||||||
alpha = 0.32f
|
alpha = 0.32f
|
||||||
@@ -412,12 +643,12 @@ fun ServerConfig(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(30.dp),
|
modifier = Size30Modifier,
|
||||||
onClick = { }
|
onClick = { onTogglePublicChats(item) }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Groups,
|
imageVector = Icons.Default.Groups,
|
||||||
stringResource(R.string.public_chat_feed),
|
contentDescription = stringResource(R.string.public_chat_feed),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 5.dp)
|
.padding(horizontal = 5.dp)
|
||||||
.size(15.dp)
|
.size(15.dp)
|
||||||
@@ -436,7 +667,7 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
tint = if (item.feedTypes.contains(FeedType.PUBLIC_CHATS)) {
|
tint = if (item.feedTypes.contains(FeedType.PUBLIC_CHATS)) {
|
||||||
Color.Green
|
MaterialTheme.colors.allGoodColor
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colors.onSurface.copy(
|
MaterialTheme.colors.onSurface.copy(
|
||||||
alpha = 0.32f
|
alpha = 0.32f
|
||||||
@@ -445,8 +676,8 @@ fun ServerConfig(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(30.dp),
|
modifier = Size30Modifier,
|
||||||
onClick = { }
|
onClick = { onToggleGlobal(item) }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Public,
|
imageVector = Icons.Default.Public,
|
||||||
@@ -469,7 +700,7 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
tint = if (item.feedTypes.contains(FeedType.GLOBAL)) {
|
tint = if (item.feedTypes.contains(FeedType.GLOBAL)) {
|
||||||
Color.Green
|
MaterialTheme.colors.allGoodColor
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colors.onSurface.copy(
|
MaterialTheme.colors.onSurface.copy(
|
||||||
alpha = 0.32f
|
alpha = 0.32f
|
||||||
@@ -479,7 +710,7 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(30.dp),
|
modifier = Size30Modifier,
|
||||||
onClick = { onToggleSearch(item) }
|
onClick = { onToggleSearch(item) }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -503,45 +734,7 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
tint = if (item.feedTypes.contains(FeedType.SEARCH)) {
|
tint = if (item.feedTypes.contains(FeedType.SEARCH)) {
|
||||||
Color.Green
|
MaterialTheme.colors.allGoodColor
|
||||||
} else {
|
|
||||||
MaterialTheme.colors.onSurface.copy(
|
|
||||||
alpha = 0.32f
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(Modifier.weight(1.4f)) {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
IconButton(
|
|
||||||
modifier = Modifier.size(30.dp),
|
|
||||||
onClick = { }
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Download,
|
|
||||||
stringResource(R.string.read_from_relay),
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 5.dp)
|
|
||||||
.size(15.dp)
|
|
||||||
.combinedClickable(
|
|
||||||
onClick = { onToggleDownload(item) },
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.read_from_relay),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
tint = if (item.read) {
|
|
||||||
Color.Green
|
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colors.onSurface.copy(
|
MaterialTheme.colors.onSurface.copy(
|
||||||
alpha = 0.32f
|
alpha = 0.32f
|
||||||
@@ -549,127 +742,49 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun FirstLine(
|
||||||
|
item: RelaySetupInfo,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onDelete: (RelaySetupInfo) -> Unit,
|
||||||
|
modifier: Modifier
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) {
|
||||||
|
Row(Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = "${countToHumanReadable(item.downloadCountInBytes)}",
|
text = item.briefInfo.displayUrl,
|
||||||
|
modifier = Modifier.clickable(onClick = onClick),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
fontSize = 12.sp,
|
overflow = TextOverflow.Ellipsis
|
||||||
modifier = Modifier.weight(1.2f),
|
|
||||||
color = MaterialTheme.colors.placeholderText
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (item.paidRelay) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Paid,
|
||||||
|
null,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 5.dp, top = 1.dp)
|
||||||
|
.size(14.dp),
|
||||||
|
tint = MaterialTheme.colors.allGoodColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(30.dp),
|
modifier = Modifier.size(30.dp),
|
||||||
onClick = { }
|
onClick = { onDelete(item) }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Upload,
|
imageVector = Icons.Default.Cancel,
|
||||||
stringResource(R.string.write_to_relay),
|
null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 5.dp)
|
.padding(start = 10.dp)
|
||||||
.size(15.dp)
|
.size(15.dp),
|
||||||
.combinedClickable(
|
tint = WarningColor
|
||||||
onClick = { onToggleUpload(item) },
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.write_to_relay),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
tint = if (item.write) {
|
|
||||||
Color.Green
|
|
||||||
} else {
|
|
||||||
MaterialTheme.colors.onSurface.copy(
|
|
||||||
alpha = 0.32f
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = "${countToHumanReadable(item.uploadCountInBytes)}",
|
|
||||||
maxLines = 1,
|
|
||||||
fontSize = 12.sp,
|
|
||||||
modifier = Modifier.weight(1.2f),
|
|
||||||
color = MaterialTheme.colors.placeholderText
|
|
||||||
)
|
|
||||||
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.SyncProblem,
|
|
||||||
stringResource(R.string.errors),
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 5.dp)
|
|
||||||
.size(15.dp)
|
|
||||||
.combinedClickable(
|
|
||||||
onClick = { },
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.errors),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
tint = if (item.errorCount > 0) Color.Yellow else Color.Green
|
|
||||||
)
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = "${countToHumanReadable(item.errorCount)}",
|
|
||||||
maxLines = 1,
|
|
||||||
fontSize = 12.sp,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
color = MaterialTheme.colors.placeholderText
|
|
||||||
)
|
|
||||||
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.DeleteSweep,
|
|
||||||
stringResource(R.string.spam),
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 5.dp)
|
|
||||||
.size(15.dp)
|
|
||||||
.combinedClickable(
|
|
||||||
onClick = { },
|
|
||||||
onLongClick = {
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.spam),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
tint = if (item.spamCount > 0) Color.Yellow else Color.Green
|
|
||||||
)
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = "${countToHumanReadable(item.spamCount)}",
|
|
||||||
maxLines = 1,
|
|
||||||
fontSize = 12.sp,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
color = MaterialTheme.colors.placeholderText
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Divider(
|
|
||||||
thickness = 0.25.dp
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,7 +817,7 @@ fun EditableServerConfig(relayToAdd: String, onNewRelay: (RelaySetupInfo) -> Uni
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(Size35dp)
|
.size(Size35dp)
|
||||||
.padding(horizontal = 5.dp),
|
.padding(horizontal = 5.dp),
|
||||||
tint = if (read) Color.Green else MaterialTheme.colors.placeholderText
|
tint = if (read) MaterialTheme.colors.allGoodColor else MaterialTheme.colors.placeholderText
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,7 +828,7 @@ fun EditableServerConfig(relayToAdd: String, onNewRelay: (RelaySetupInfo) -> Uni
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(Size35dp)
|
.size(Size35dp)
|
||||||
.padding(horizontal = 5.dp),
|
.padding(horizontal = 5.dp),
|
||||||
tint = if (write) Color.Green else MaterialTheme.colors.placeholderText
|
tint = if (write) MaterialTheme.colors.allGoodColor else MaterialTheme.colors.placeholderText
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,9 +854,16 @@ fun EditableServerConfig(relayToAdd: String, onNewRelay: (RelaySetupInfo) -> Uni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun countToHumanReadable(counter: Int) = when {
|
fun countToHumanReadableBytes(counter: Int) = when {
|
||||||
counter >= 1000000000 -> "${round(counter / 1000000000f)}G"
|
counter >= 1000000000 -> "${round(counter / 1000000000f)} GB"
|
||||||
counter >= 1000000 -> "${round(counter / 1000000f)}M"
|
counter >= 1000000 -> "${round(counter / 1000000f)} MB"
|
||||||
counter >= 1000 -> "${round(counter / 1000f)}k"
|
counter >= 1000 -> "${round(counter / 1000f)} KB"
|
||||||
else -> "$counter"
|
else -> "$counter"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun countToHumanReadable(counter: Int, str: String) = when {
|
||||||
|
counter >= 1000000000 -> "${round(counter / 1000000000f)}G $str"
|
||||||
|
counter >= 1000000 -> "${round(counter / 1000000f)}M $str"
|
||||||
|
counter >= 1000 -> "${round(counter / 1000f)}K $str"
|
||||||
|
else -> "$counter $str"
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
||||||
|
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||||
import com.vitorpamplona.amethyst.service.relays.Constants
|
import com.vitorpamplona.amethyst.service.relays.Constants
|
||||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||||
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
||||||
@@ -24,6 +25,7 @@ class NewRelayListViewModel : ViewModel() {
|
|||||||
fun load(account: Account) {
|
fun load(account: Account) {
|
||||||
this.account = account
|
this.account = account
|
||||||
clear()
|
clear()
|
||||||
|
loadRelayDocuments()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create() {
|
fun create() {
|
||||||
@@ -36,6 +38,21 @@ class NewRelayListViewModel : ViewModel() {
|
|||||||
clear()
|
clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadRelayDocuments() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_relays.value.forEach { item ->
|
||||||
|
Nip11CachedRetriever.loadRelayInfo(
|
||||||
|
dirtyUrl = item.url,
|
||||||
|
onInfo = {
|
||||||
|
togglePaidRelay(item, it.limitation?.payment_required ?: false)
|
||||||
|
},
|
||||||
|
onError = { url, errorCode, exceptionMessage ->
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
_relays.update {
|
_relays.update {
|
||||||
var relayFile = account.userProfile().latestContactList?.relays()
|
var relayFile = account.userProfile().latestContactList?.relays()
|
||||||
|
|||||||
+32
-108
@@ -1,8 +1,5 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.actions
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -28,27 +25,24 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.model.RelayBriefInfo
|
||||||
import com.vitorpamplona.amethyst.model.RelayInformation
|
import com.vitorpamplona.amethyst.model.RelayInformation
|
||||||
import com.vitorpamplona.amethyst.service.HttpClient
|
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
|
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
|
||||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||||
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||||
import kotlinx.coroutines.launch
|
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||||
import okhttp3.Call
|
|
||||||
import okhttp3.Callback
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RelayInformationDialog(
|
fun RelayInformationDialog(
|
||||||
onClose: () -> Unit,
|
onClose: () -> Unit,
|
||||||
|
relayBriefInfo: RelayBriefInfo,
|
||||||
relayInfo: RelayInformation,
|
relayInfo: RelayInformation,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
@@ -79,18 +73,25 @@ fun RelayInformationDialog(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, modifier = StdPadding.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Column() {
|
||||||
horizontalArrangement = Arrangement.Center
|
RenderRelayIcon(
|
||||||
) {
|
relayBriefInfo.favIcon,
|
||||||
Title(relayInfo.name ?: "")
|
Size55dp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Spacer(modifier = DoubleHorzSpacer)
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.Center
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
) {
|
Row() {
|
||||||
SectionContent(relayInfo.description ?: "")
|
Title(relayInfo.name?.trim() ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
Row() {
|
||||||
|
SubtitleContent(relayInfo.description?.trim() ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(stringResource(R.string.owner))
|
Section(stringResource(R.string.owner))
|
||||||
@@ -212,7 +213,7 @@ private fun DisplaySupportedNips(relayInfo: RelayInformation) {
|
|||||||
val text = item.toString().padStart(2, '0')
|
val text = item.toString().padStart(2, '0')
|
||||||
Box(Modifier.padding(10.dp)) {
|
Box(Modifier.padding(10.dp)) {
|
||||||
ClickableUrl(
|
ClickableUrl(
|
||||||
urlText = "$text",
|
urlText = text,
|
||||||
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md"
|
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -222,7 +223,7 @@ private fun DisplaySupportedNips(relayInfo: RelayInformation) {
|
|||||||
val text = item.padStart(2, '0')
|
val text = item.padStart(2, '0')
|
||||||
Box(Modifier.padding(10.dp)) {
|
Box(Modifier.padding(10.dp)) {
|
||||||
ClickableUrl(
|
ClickableUrl(
|
||||||
urlText = "$text",
|
urlText = text,
|
||||||
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md"
|
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -258,13 +259,18 @@ private fun DisplayOwnerInformation(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Title(text: String) {
|
fun Title(text: String) {
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 24.sp
|
fontSize = 24.sp
|
||||||
)
|
)
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SubtitleContent(text: String) {
|
||||||
|
Text(
|
||||||
|
text = text
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -285,85 +291,3 @@ fun SectionContent(text: String) {
|
|||||||
text = text
|
text = text
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadRelayInfo(
|
|
||||||
dirtyUrl: String,
|
|
||||||
context: Context,
|
|
||||||
scope: CoroutineScope,
|
|
||||||
onInfo: (RelayInformation) -> Unit
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
val url = if (dirtyUrl.contains("://")) {
|
|
||||||
dirtyUrl
|
|
||||||
.replace("wss://", "https://")
|
|
||||||
.replace("ws://", "http://")
|
|
||||||
} else {
|
|
||||||
"https://$dirtyUrl"
|
|
||||||
}
|
|
||||||
|
|
||||||
val request: Request = Request
|
|
||||||
.Builder()
|
|
||||||
.header("Accept", "application/nostr+json")
|
|
||||||
.url(url)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
HttpClient.getHttpClient()
|
|
||||||
.newCall(request)
|
|
||||||
.enqueue(
|
|
||||||
object : Callback {
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
|
||||||
checkNotInMainThread()
|
|
||||||
response.use {
|
|
||||||
val body = it.body.string()
|
|
||||||
try {
|
|
||||||
if (it.isSuccessful) {
|
|
||||||
onInfo(RelayInformation.fromJson(body))
|
|
||||||
} else {
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.an_error_occurred_trying_to_get_relay_information, dirtyUrl),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("RelayInfoFail", "Resulting Message from Relay $dirtyUrl in not parseable: $body", e)
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.an_error_occurred_trying_to_get_relay_information, dirtyUrl),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
|
||||||
Log.e("RelayInfoFail", "$dirtyUrl unavailable", e)
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.an_error_occurred_trying_to_get_relay_information, dirtyUrl),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("RelayInfoFail", "Invalid URL $dirtyUrl", e)
|
|
||||||
scope.launch {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.an_error_occurred_trying_to_get_relay_information, dirtyUrl),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.window.DialogProperties
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.RelayBriefInfo
|
import com.vitorpamplona.amethyst.model.RelayBriefInfo
|
||||||
import com.vitorpamplona.amethyst.model.RelayInformation
|
import com.vitorpamplona.amethyst.model.RelayInformation
|
||||||
|
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -41,6 +42,11 @@ data class RelayList(
|
|||||||
val isSelected: Boolean
|
val isSelected: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class RelayInfoDialog(
|
||||||
|
val relayBriefInfo: RelayBriefInfo,
|
||||||
|
val relayInfo: RelayInformation
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RelaySelectionDialog(
|
fun RelaySelectionDialog(
|
||||||
preSelectedList: List<Relay>,
|
preSelectedList: List<Relay>,
|
||||||
@@ -63,16 +69,17 @@ fun RelaySelectionDialog(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var relayInfo: RelayInformation? by remember { mutableStateOf(null) }
|
var relayInfo: RelayInfoDialog? by remember { mutableStateOf(null) }
|
||||||
|
|
||||||
if (relayInfo != null) {
|
relayInfo?.let {
|
||||||
RelayInformationDialog(
|
RelayInformationDialog(
|
||||||
onClose = {
|
onClose = {
|
||||||
relayInfo = null
|
relayInfo = null
|
||||||
},
|
},
|
||||||
relayInfo = relayInfo!!,
|
relayInfo = it.relayInfo,
|
||||||
accountViewModel,
|
relayBriefInfo = it.relayBriefInfo,
|
||||||
nav
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +168,30 @@ fun RelaySelectionDialog(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress = {
|
onLongPress = {
|
||||||
loadRelayInfo(item.relay.url, context, scope) {
|
accountViewModel.retrieveRelayDocument(
|
||||||
relayInfo = it
|
item.relay.url,
|
||||||
|
onInfo = {
|
||||||
|
relayInfo = RelayInfoDialog(RelayBriefInfo(item.relay.url), it)
|
||||||
|
},
|
||||||
|
onError = { url, errorCode, exceptionMessage ->
|
||||||
|
val msg = when (errorCode) {
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
msg,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.note
|
package com.vitorpamplona.amethyst.ui.note
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@@ -28,14 +29,15 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.map
|
import androidx.lifecycle.map
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.RelayBriefInfo
|
import com.vitorpamplona.amethyst.model.RelayBriefInfo
|
||||||
import com.vitorpamplona.amethyst.model.RelayInformation
|
import com.vitorpamplona.amethyst.model.RelayInformation
|
||||||
|
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||||
import com.vitorpamplona.amethyst.ui.actions.RelayInformationDialog
|
import com.vitorpamplona.amethyst.ui.actions.RelayInformationDialog
|
||||||
import com.vitorpamplona.amethyst.ui.actions.loadRelayInfo
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
|
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
|
||||||
@@ -45,6 +47,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size15dp
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.StdStartPadding
|
import com.vitorpamplona.amethyst.ui.theme.StdStartPadding
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
public fun RelayBadgesHorizontal(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
public fun RelayBadgesHorizontal(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
@@ -112,8 +115,9 @@ fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav:
|
|||||||
relayInfo = null
|
relayInfo = null
|
||||||
},
|
},
|
||||||
relayInfo = relayInfo!!,
|
relayInfo = relayInfo!!,
|
||||||
accountViewModel,
|
relayBriefInfo = relay,
|
||||||
nav
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,9 +135,30 @@ fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav:
|
|||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
indication = ripple,
|
indication = ripple,
|
||||||
onClick = {
|
onClick = {
|
||||||
loadRelayInfo(relay.url, context, scope) {
|
accountViewModel.retrieveRelayDocument(
|
||||||
|
relay.url,
|
||||||
|
onInfo = {
|
||||||
relayInfo = it
|
relayInfo = it
|
||||||
|
},
|
||||||
|
onError = { url, errorCode, exceptionMessage ->
|
||||||
|
val msg = when (errorCode) {
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
|
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
msg,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -146,12 +171,12 @@ fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RenderRelayIcon(iconUrl: String) {
|
fun RenderRelayIcon(iconUrl: String, size: Dp = Size13dp) {
|
||||||
val backgroundColor = MaterialTheme.colors.background
|
val backgroundColor = MaterialTheme.colors.background
|
||||||
|
|
||||||
val iconModifier = remember {
|
val iconModifier = remember {
|
||||||
Modifier
|
Modifier
|
||||||
.size(Size13dp)
|
.size(size)
|
||||||
.clip(shape = CircleShape)
|
.clip(shape = CircleShape)
|
||||||
.background(backgroundColor)
|
.background(backgroundColor)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ import com.vitorpamplona.amethyst.model.AddressableNote
|
|||||||
import com.vitorpamplona.amethyst.model.ConnectivityType
|
import com.vitorpamplona.amethyst.model.ConnectivityType
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
|
import com.vitorpamplona.amethyst.model.RelayInformation
|
||||||
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.model.UserState
|
import com.vitorpamplona.amethyst.model.UserState
|
||||||
import com.vitorpamplona.amethyst.service.Nip05Verifier
|
import com.vitorpamplona.amethyst.service.Nip05Verifier
|
||||||
|
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||||
|
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||||
import com.vitorpamplona.amethyst.service.OnlineChecker
|
import com.vitorpamplona.amethyst.service.OnlineChecker
|
||||||
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
||||||
import com.vitorpamplona.amethyst.ui.components.UrlPreviewState
|
import com.vitorpamplona.amethyst.ui.components.UrlPreviewState
|
||||||
@@ -551,6 +554,16 @@ class AccountViewModel(val account: Account) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun retrieveRelayDocument(
|
||||||
|
dirtyUrl: String,
|
||||||
|
onInfo: (RelayInformation) -> Unit,
|
||||||
|
onError: (String, Nip11Retriever.ErrorCode, String?) -> Unit
|
||||||
|
) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
Nip11CachedRetriever.loadRelayInfo(dirtyUrl, onInfo, onError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||||
override fun <AccountViewModel : ViewModel> create(modelClass: Class<AccountViewModel>): AccountViewModel {
|
override fun <AccountViewModel : ViewModel> create(modelClass: Class<AccountViewModel>): AccountViewModel {
|
||||||
return AccountViewModel(account) as AccountViewModel
|
return AccountViewModel(account) as AccountViewModel
|
||||||
|
|||||||
@@ -24,3 +24,9 @@ val DarkerGreen = Color.Green.copy(alpha = 0.32f)
|
|||||||
val WarningColor = Color(0xFFC62828)
|
val WarningColor = Color(0xFFC62828)
|
||||||
|
|
||||||
val RelayIconFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0.5f) })
|
val RelayIconFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0.5f) })
|
||||||
|
|
||||||
|
val LightWarningColor = Color(0xFFffcc00)
|
||||||
|
val DarkWarningColor = Color(0xFFF8DE22)
|
||||||
|
|
||||||
|
val LightAllGoodColor = Color(0xFF339900)
|
||||||
|
val DarkAllGoodColor = Color(0xFF99cc33)
|
||||||
|
|||||||
@@ -286,6 +286,12 @@ val Colors.overPictureBackground: Color
|
|||||||
val Colors.bitcoinColor: Color
|
val Colors.bitcoinColor: Color
|
||||||
get() = if (isLight) BitcoinLight else BitcoinDark
|
get() = if (isLight) BitcoinLight else BitcoinDark
|
||||||
|
|
||||||
|
val Colors.warningColor: Color
|
||||||
|
get() = if (isLight) LightWarningColor else DarkWarningColor
|
||||||
|
|
||||||
|
val Colors.allGoodColor: Color
|
||||||
|
get() = if (isLight) LightAllGoodColor else DarkAllGoodColor
|
||||||
|
|
||||||
val Colors.markdownStyle: RichTextStyle
|
val Colors.markdownStyle: RichTextStyle
|
||||||
get() = if (isLight) MarkDownStyleOnLight else MarkDownStyleOnDark
|
get() = if (isLight) MarkDownStyleOnLight else MarkDownStyleOnDark
|
||||||
|
|
||||||
|
|||||||
@@ -558,4 +558,16 @@
|
|||||||
<string name="error_dialog_zap_error">Unable to send zap</string>
|
<string name="error_dialog_zap_error">Unable to send zap</string>
|
||||||
<string name="error_dialog_talk_to_user">Message the User</string>
|
<string name="error_dialog_talk_to_user">Message the User</string>
|
||||||
<string name="error_dialog_button_ok">Ok</string>
|
<string name="error_dialog_button_ok">Ok</string>
|
||||||
|
|
||||||
|
<string name="relay_information_document_error_assemble_url">Failed to reach %1$s: %2$s</string>
|
||||||
|
<string name="relay_information_document_error_reach_server">Failed to reach %1$s: %2$s</string>
|
||||||
|
<string name="relay_information_document_error_parse_result">Failed to parse result from %1$s: %2$s</string>
|
||||||
|
<string name="relay_information_document_error_http_status">%1$s failed with code %2$s</string>
|
||||||
|
<string name="active_for">Active for: </string>
|
||||||
|
|
||||||
|
<string name="active_for_home">Home</string>
|
||||||
|
<string name="active_for_msg">DMs</string>
|
||||||
|
<string name="active_for_chats">Chats</string>
|
||||||
|
<string name="active_for_global">Global</string>
|
||||||
|
<string name="active_for_search">Search</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user