Merge pull request #462 from greenart7c3/relay_information
Relay information dialog
This commit is contained in:
@@ -0,0 +1,250 @@
|
|||||||
|
package com.vitorpamplona.amethyst.model
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonSerializationContext
|
||||||
|
import com.google.gson.JsonSerializer
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
class RelayInformation(
|
||||||
|
val name: String?,
|
||||||
|
val description: String?,
|
||||||
|
val pubkey: String?,
|
||||||
|
val contact: String?,
|
||||||
|
val supported_nips: List<Int>?,
|
||||||
|
val supported_nip_extensions: List<String>?,
|
||||||
|
val software: String?,
|
||||||
|
val version: String?,
|
||||||
|
val limitation: RelayInformationLimitation?,
|
||||||
|
val relay_countries: List<String>?,
|
||||||
|
val language_tags: List<String>?,
|
||||||
|
val tags: List<String>?,
|
||||||
|
val posting_policy: String?,
|
||||||
|
val payments_url: String?,
|
||||||
|
val fees: RelayInformationFees?
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val gson: Gson = GsonBuilder()
|
||||||
|
.disableHtmlEscaping()
|
||||||
|
.registerTypeAdapter(RelayInformation::class.java, RelayInformationSerializer())
|
||||||
|
.registerTypeAdapter(RelayInformationLimitation::class.java, RelayInformationLimitationSerializer())
|
||||||
|
.registerTypeAdapter(RelayInformationFees::class.java, RelayInformationFeesSerializer())
|
||||||
|
.registerTypeAdapter(RelayInformationFee::class.java, RelayInformationFeeSerializer())
|
||||||
|
.create()
|
||||||
|
|
||||||
|
fun fromJson(json: String): RelayInformation = gson.fromJson(json, RelayInformation::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RelayInformationFee(
|
||||||
|
val amount: Int?,
|
||||||
|
val unit: String?,
|
||||||
|
val period: Int?,
|
||||||
|
val kinds: List<Int>?
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val gson: Gson = GsonBuilder()
|
||||||
|
.disableHtmlEscaping()
|
||||||
|
.registerTypeAdapter(RelayInformationFee::class.java, RelayInformationFeeSerializer())
|
||||||
|
.create()
|
||||||
|
|
||||||
|
fun fromJson(json: String): RelayInformationFee = gson.fromJson(json, RelayInformationFee::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RelayInformationFeeSerializer : JsonSerializer<RelayInformationFee> {
|
||||||
|
override fun serialize(
|
||||||
|
src: RelayInformationFee,
|
||||||
|
typeOfSrc: Type?,
|
||||||
|
context: JsonSerializationContext?
|
||||||
|
): JsonElement {
|
||||||
|
return JsonObject().apply {
|
||||||
|
addProperty("amount", src.amount)
|
||||||
|
addProperty("unit", src.unit)
|
||||||
|
addProperty("period", src.period)
|
||||||
|
add(
|
||||||
|
"kinds",
|
||||||
|
JsonArray().also { kinds ->
|
||||||
|
src.kinds?.forEach { kind ->
|
||||||
|
kinds.add(
|
||||||
|
kind
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RelayInformationFees(
|
||||||
|
val admission: List<RelayInformationFee>?,
|
||||||
|
val subscription: List<RelayInformationFee>?,
|
||||||
|
val publication: List<RelayInformationFee>?
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val gson: Gson = GsonBuilder()
|
||||||
|
.disableHtmlEscaping()
|
||||||
|
.registerTypeAdapter(RelayInformationFees::class.java, RelayInformationFeesSerializer())
|
||||||
|
.create()
|
||||||
|
|
||||||
|
fun fromJson(json: String): RelayInformationFees = gson.fromJson(json, RelayInformationFees::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RelayInformationFeesSerializer : JsonSerializer<RelayInformationFees> {
|
||||||
|
override fun serialize(
|
||||||
|
src: RelayInformationFees,
|
||||||
|
typeOfSrc: Type?,
|
||||||
|
context: JsonSerializationContext?
|
||||||
|
): JsonElement {
|
||||||
|
return JsonObject().apply {
|
||||||
|
add(
|
||||||
|
"admission",
|
||||||
|
JsonArray().also { admissions ->
|
||||||
|
src.admission?.forEach { admission ->
|
||||||
|
admissions.add(
|
||||||
|
admission.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
"publication",
|
||||||
|
JsonArray().also { publications ->
|
||||||
|
src.publication?.forEach { publication ->
|
||||||
|
publications.add(
|
||||||
|
publication.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
"subscription",
|
||||||
|
JsonArray().also { subscriptions ->
|
||||||
|
src.subscription?.forEach { subscription ->
|
||||||
|
subscriptions.add(
|
||||||
|
subscription.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RelayInformationLimitation(
|
||||||
|
val max_message_length: Int?,
|
||||||
|
val max_subscriptions: Int?,
|
||||||
|
val max_filters: Int?,
|
||||||
|
val max_limit: Int?,
|
||||||
|
val max_subid_length: Int?,
|
||||||
|
val min_prefix: Int?,
|
||||||
|
val max_event_tags: Int?,
|
||||||
|
val max_content_length: Int?,
|
||||||
|
val min_pow_difficulty: Int?,
|
||||||
|
val auth_required: Boolean?,
|
||||||
|
val payment_required: Boolean?
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val gson: Gson = GsonBuilder()
|
||||||
|
.disableHtmlEscaping()
|
||||||
|
.registerTypeAdapter(RelayInformationLimitation::class.java, RelayInformationLimitationSerializer())
|
||||||
|
.create()
|
||||||
|
|
||||||
|
fun fromJson(json: String): RelayInformationLimitation = gson.fromJson(json, RelayInformationLimitation::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RelayInformationLimitationSerializer : JsonSerializer<RelayInformationLimitation> {
|
||||||
|
override fun serialize(
|
||||||
|
src: RelayInformationLimitation,
|
||||||
|
typeOfSrc: Type?,
|
||||||
|
context: JsonSerializationContext?
|
||||||
|
): JsonElement {
|
||||||
|
return JsonObject().apply {
|
||||||
|
addProperty("max_message_length", src.max_message_length)
|
||||||
|
addProperty("max_subscriptions", src.max_subscriptions)
|
||||||
|
addProperty("max_filters", src.max_filters)
|
||||||
|
addProperty("max_limit", src.max_limit)
|
||||||
|
addProperty("max_subid_length", src.max_subid_length)
|
||||||
|
addProperty("min_prefix", src.min_prefix)
|
||||||
|
addProperty("max_event_tags", src.max_event_tags)
|
||||||
|
addProperty("max_content_length", src.max_content_length)
|
||||||
|
addProperty("min_pow_difficulty", src.min_pow_difficulty)
|
||||||
|
addProperty("auth_required", src.auth_required)
|
||||||
|
addProperty("payment_required", src.payment_required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RelayInformationSerializer : JsonSerializer<RelayInformation> {
|
||||||
|
override fun serialize(
|
||||||
|
src: RelayInformation,
|
||||||
|
typeOfSrc: Type?,
|
||||||
|
context: JsonSerializationContext?
|
||||||
|
): JsonElement {
|
||||||
|
return JsonObject().apply {
|
||||||
|
addProperty("name", src.name)
|
||||||
|
addProperty("description", src.description)
|
||||||
|
addProperty("pubkey", src.pubkey)
|
||||||
|
addProperty("contact", src.contact)
|
||||||
|
add(
|
||||||
|
"supported_nip_extensions",
|
||||||
|
JsonArray().also { supported_nip_extensions ->
|
||||||
|
src.supported_nip_extensions?.forEach { nip ->
|
||||||
|
supported_nip_extensions.add(
|
||||||
|
nip
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
"supported_nips",
|
||||||
|
JsonArray().also { supported_nips ->
|
||||||
|
src.supported_nips?.forEach { nip ->
|
||||||
|
supported_nips.add(
|
||||||
|
nip
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
addProperty("software", src.software)
|
||||||
|
addProperty("version", src.version)
|
||||||
|
add(
|
||||||
|
"relay_countries",
|
||||||
|
JsonArray().also { relay_countries ->
|
||||||
|
src.relay_countries?.forEach { country ->
|
||||||
|
relay_countries.add(
|
||||||
|
country
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
"language_tags",
|
||||||
|
JsonArray().also { language_tags ->
|
||||||
|
src.language_tags?.forEach { language_tag ->
|
||||||
|
language_tags.add(
|
||||||
|
language_tag
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
"tags",
|
||||||
|
JsonArray().also { tags ->
|
||||||
|
src.tags?.forEach { tag ->
|
||||||
|
tags.add(
|
||||||
|
tag
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
addProperty("posting_policy", src.posting_policy)
|
||||||
|
addProperty("payments_url", src.payments_url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.actions
|
|||||||
|
|
||||||
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.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -53,17 +54,25 @@ 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.LocalCache
|
||||||
|
import com.vitorpamplona.amethyst.model.RelayInformation
|
||||||
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
||||||
|
import com.vitorpamplona.amethyst.service.HttpClient
|
||||||
|
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
||||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||||
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.Size35dp
|
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
import java.lang.Math.round
|
import java.lang.Math.round
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, relayToAdd: String = "") {
|
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()
|
||||||
|
|
||||||
@@ -125,7 +134,9 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
|
|||||||
onToggleGlobal = { postViewModel.toggleGlobal(it) },
|
onToggleGlobal = { postViewModel.toggleGlobal(it) },
|
||||||
onToggleSearch = { postViewModel.toggleSearch(it) },
|
onToggleSearch = { postViewModel.toggleSearch(it) },
|
||||||
|
|
||||||
onDelete = { postViewModel.deleteRelay(it) }
|
onDelete = { postViewModel.deleteRelay(it) },
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,10 +233,31 @@ fun ServerConfig(
|
|||||||
onToggleGlobal: (RelaySetupInfo) -> Unit,
|
onToggleGlobal: (RelaySetupInfo) -> Unit,
|
||||||
onToggleSearch: (RelaySetupInfo) -> Unit,
|
onToggleSearch: (RelaySetupInfo) -> Unit,
|
||||||
|
|
||||||
onDelete: (RelaySetupInfo) -> Unit
|
onDelete: (RelaySetupInfo) -> Unit,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
var relayInfo: RelayInformation? by remember { mutableStateOf(null) }
|
||||||
|
|
||||||
|
if (relayInfo != null) {
|
||||||
|
val user = LocalCache.getOrCreateUser(relayInfo!!.pubkey ?: "")
|
||||||
|
NostrUserProfileDataSource.loadUserProfile(user)
|
||||||
|
NostrUserProfileDataSource.start()
|
||||||
|
RelayInformationDialog(
|
||||||
|
onClose = {
|
||||||
|
relayInfo = null
|
||||||
|
NostrUserProfileDataSource.loadUserProfile(null)
|
||||||
|
NostrUserProfileDataSource.stop()
|
||||||
|
},
|
||||||
|
relayInfo = relayInfo!!,
|
||||||
|
user,
|
||||||
|
accountViewModel,
|
||||||
|
nav
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -251,7 +283,52 @@ fun ServerConfig(
|
|||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = item.url.removePrefix("wss://"),
|
text = item.url.removePrefix("wss://"),
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.clickable {
|
||||||
|
val client = HttpClient.getHttpClient()
|
||||||
|
val url = item.url
|
||||||
|
.replace("wss://", "https://")
|
||||||
|
.replace("ws://", "http://")
|
||||||
|
val request: Request = Request
|
||||||
|
.Builder()
|
||||||
|
.header("Accept", "application/nostr+json")
|
||||||
|
.url(url)
|
||||||
|
.build()
|
||||||
|
client
|
||||||
|
.newCall(request)
|
||||||
|
.enqueue(object : Callback {
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
response.use {
|
||||||
|
if (it.isSuccessful) {
|
||||||
|
relayInfo =
|
||||||
|
RelayInformation.fromJson(it.body.string())
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.an_error_ocurred_trying_to_get_relay_information),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call, e: java.io.IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
scope.launch {
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.an_error_ocurred_trying_to_get_relay_information),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
@@ -275,11 +352,13 @@ fun ServerConfig(
|
|||||||
onClick = { onToggleFollows(item) },
|
onClick = { onToggleFollows(item) },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.home_feed),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.home_feed),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -306,11 +385,13 @@ fun ServerConfig(
|
|||||||
onClick = { onTogglePrivateDMs(item) },
|
onClick = { onTogglePrivateDMs(item) },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.private_message_feed),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.private_message_feed),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -337,11 +418,13 @@ fun ServerConfig(
|
|||||||
onClick = { onTogglePublicChats(item) },
|
onClick = { onTogglePublicChats(item) },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.public_chat_feed),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.public_chat_feed),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -368,11 +451,13 @@ fun ServerConfig(
|
|||||||
onClick = { onToggleGlobal(item) },
|
onClick = { onToggleGlobal(item) },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.global_feed),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.global_feed),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -400,11 +485,13 @@ fun ServerConfig(
|
|||||||
onClick = { onToggleSearch(item) },
|
onClick = { onToggleSearch(item) },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.search_feed),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.search_feed),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -436,11 +523,13 @@ fun ServerConfig(
|
|||||||
onClick = { onToggleDownload(item) },
|
onClick = { onToggleDownload(item) },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.read_from_relay),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.read_from_relay),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -476,11 +565,13 @@ fun ServerConfig(
|
|||||||
onClick = { onToggleUpload(item) },
|
onClick = { onToggleUpload(item) },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.write_to_relay),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.write_to_relay),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -512,11 +603,13 @@ fun ServerConfig(
|
|||||||
onClick = { },
|
onClick = { },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.errors),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.errors),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -541,11 +634,13 @@ fun ServerConfig(
|
|||||||
onClick = { },
|
onClick = { },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast
|
||||||
context,
|
.makeText(
|
||||||
context.getString(R.string.spam),
|
context,
|
||||||
Toast.LENGTH_SHORT
|
context.getString(R.string.spam),
|
||||||
).show()
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,370 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
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.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.text.ClickableText
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.LocalTextStyle
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Downloading
|
||||||
|
import androidx.compose.material.icons.filled.Report
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
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.LocalUriHandler
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
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.RelayInformation
|
||||||
|
import com.vitorpamplona.amethyst.model.User
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.nip05VerificationAsAState
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.DisplayLNAddress
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Nip05
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Section(text: String) {
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 25.sp
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SectionContent(text: String) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = 10.dp),
|
||||||
|
text = text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun RelayInformationDialog(onClose: () -> Unit, relayInfo: RelayInformation, baseUser: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
|
val userState by baseUser.live().metadata.observeAsState()
|
||||||
|
val user = remember(userState) { userState?.user } ?: return
|
||||||
|
val tags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() }
|
||||||
|
val lud16 = remember(userState) { user.info?.lud16?.trim() ?: user.info?.lud06?.trim() }
|
||||||
|
val pubkeyHex = remember { baseUser.pubkeyHex }
|
||||||
|
val uri = LocalUriHandler.current
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = { onClose() },
|
||||||
|
properties = DialogProperties(
|
||||||
|
usePlatformDefaultWidth = false,
|
||||||
|
dismissOnClickOutside = false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Surface {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(10.dp)
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(scrollState)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
CloseButton(onCancel = {
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Section(relayInfo.name ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
SectionContent(relayInfo.description ?: "")
|
||||||
|
|
||||||
|
Section(stringResource(R.string.owner))
|
||||||
|
|
||||||
|
Row {
|
||||||
|
UserPicture(
|
||||||
|
baseUser = user,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
size = 100.dp,
|
||||||
|
modifier = Modifier.border(
|
||||||
|
3.dp,
|
||||||
|
MaterialTheme.colors.background,
|
||||||
|
CircleShape
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
nav("User/${user.pubkeyHex}")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(Modifier.padding(start = 10.dp)) {
|
||||||
|
(user.bestDisplayName() ?: user.bestUsername())?.let {
|
||||||
|
Row(verticalAlignment = Alignment.Bottom, modifier = Modifier.padding(top = 7.dp)) {
|
||||||
|
CreateTextWithEmoji(
|
||||||
|
text = it,
|
||||||
|
tags = tags,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 25.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.bestDisplayName() != null) {
|
||||||
|
user.bestUsername()?.let {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp)
|
||||||
|
) {
|
||||||
|
CreateTextWithEmoji(
|
||||||
|
text = "@$it",
|
||||||
|
tags = tags,
|
||||||
|
color = MaterialTheme.colors.placeholderText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user.nip05()?.let { nip05 ->
|
||||||
|
if (nip05.split("@").size == 2) {
|
||||||
|
val nip05Verified by nip05VerificationAsAState(user.info!!, user.pubkeyHex)
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
if (nip05Verified == null) {
|
||||||
|
Icon(
|
||||||
|
tint = Color.Yellow,
|
||||||
|
imageVector = Icons.Default.Downloading,
|
||||||
|
contentDescription = "Downloading",
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
} else if (nip05Verified == true) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_verified),
|
||||||
|
"NIP-05 Verified",
|
||||||
|
tint = Nip05,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
tint = Color.Red,
|
||||||
|
imageVector = Icons.Default.Report,
|
||||||
|
contentDescription = "Invalid Nip05",
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var domainPadStart = 5.dp
|
||||||
|
|
||||||
|
if (nip05.split("@")[0] != "_") {
|
||||||
|
Text(
|
||||||
|
text = AnnotatedString(nip05.split("@")[0] + "@"),
|
||||||
|
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
domainPadStart = 0.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
ClickableText(
|
||||||
|
text = AnnotatedString(nip05.split("@")[1]),
|
||||||
|
onClick = { nip05.let { runCatching { uri.openUri("https://${it.split("@")[1]}") } } },
|
||||||
|
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary),
|
||||||
|
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = domainPadStart),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayLNAddress(lud16, pubkeyHex, accountViewModel.account)
|
||||||
|
|
||||||
|
user.info?.about?.let {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
|
||||||
|
) {
|
||||||
|
val defaultBackground = MaterialTheme.colors.background
|
||||||
|
val background = remember {
|
||||||
|
mutableStateOf(defaultBackground)
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslatableRichTextViewer(
|
||||||
|
content = it,
|
||||||
|
canPreview = false,
|
||||||
|
tags = remember { ImmutableListOfLists(emptyList()) },
|
||||||
|
backgroundColor = background,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(stringResource(R.string.software))
|
||||||
|
|
||||||
|
val url = (relayInfo.software ?: "").replace("git+", "")
|
||||||
|
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||||
|
ClickableUrl(
|
||||||
|
urlText = url,
|
||||||
|
url = url
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(stringResource(R.string.version))
|
||||||
|
|
||||||
|
SectionContent(relayInfo.version ?: "")
|
||||||
|
|
||||||
|
Section(stringResource(R.string.contact))
|
||||||
|
|
||||||
|
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||||
|
ClickableEmail(relayInfo.contact ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(stringResource(R.string.supports))
|
||||||
|
|
||||||
|
FlowRow {
|
||||||
|
relayInfo.supported_nips?.forEach { item ->
|
||||||
|
val text = item.toString().padStart(2, '0')
|
||||||
|
Box(Modifier.padding(10.dp)) {
|
||||||
|
ClickableUrl(
|
||||||
|
urlText = "Nip-$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 = "Nip-$text",
|
||||||
|
url = "https://github.com/nostr-protocol/nips/blob/master/$text.md"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relayInfo.fees?.admission?.let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
Section(stringResource(R.string.admission_fees))
|
||||||
|
|
||||||
|
it.forEach { item ->
|
||||||
|
SectionContent("${item.amount?.div(1000) ?: 0} sats")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relayInfo.payments_url?.let {
|
||||||
|
Section(stringResource(R.string.payments_url))
|
||||||
|
|
||||||
|
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||||
|
ClickableUrl(
|
||||||
|
urlText = it,
|
||||||
|
url = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relayInfo.limitation?.let {
|
||||||
|
Section(stringResource(R.string.limitations))
|
||||||
|
val authRequired = it.auth_required ?: false
|
||||||
|
val authRequiredText = if (authRequired) stringResource(R.string.yes) else stringResource(R.string.no)
|
||||||
|
val paymentRequired = it.payment_required ?: false
|
||||||
|
val paymentRequiredText = if (paymentRequired) stringResource(R.string.yes) else stringResource(R.string.no)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
SectionContent("${stringResource(R.string.message_length)}: ${it.max_message_length ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.subscriptions)}: ${it.max_subscriptions ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.filters)}: ${it.max_subscriptions ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.subscription_id_length)}: ${it.max_subid_length ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.minimum_prefix)}: ${it.min_prefix ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.maximum_event_tags)}: ${it.max_event_tags ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.content_length)}: ${it.max_content_length ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.minimum_pow)}: ${it.min_pow_difficulty ?: 0}")
|
||||||
|
SectionContent("${stringResource(R.string.auth)}: $authRequiredText")
|
||||||
|
SectionContent("${stringResource(R.string.payment)}: $paymentRequiredText")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relayInfo.relay_countries?.let {
|
||||||
|
Section(stringResource(R.string.countries))
|
||||||
|
|
||||||
|
FlowRow {
|
||||||
|
it.forEach { item ->
|
||||||
|
SectionContent(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relayInfo.language_tags?.let {
|
||||||
|
Section(stringResource(R.string.languages))
|
||||||
|
|
||||||
|
FlowRow {
|
||||||
|
it.forEach { item ->
|
||||||
|
SectionContent(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relayInfo.tags?.let {
|
||||||
|
Section(stringResource(R.string.tags))
|
||||||
|
|
||||||
|
FlowRow {
|
||||||
|
it.forEach { item ->
|
||||||
|
SectionContent(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relayInfo.posting_policy?.let {
|
||||||
|
Section(stringResource(R.string.posting_policy))
|
||||||
|
|
||||||
|
Box(Modifier.padding(10.dp)) {
|
||||||
|
ClickableUrl(
|
||||||
|
it,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,7 +95,8 @@ fun AppTopBar(
|
|||||||
followLists: FollowListViewModel,
|
followLists: FollowListViewModel,
|
||||||
navEntryState: State<NavBackStackEntry?>,
|
navEntryState: State<NavBackStackEntry?>,
|
||||||
scaffoldState: ScaffoldState,
|
scaffoldState: ScaffoldState,
|
||||||
accountViewModel: AccountViewModel
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val currentRoute by remember(navEntryState.value) {
|
val currentRoute by remember(navEntryState.value) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
@@ -103,7 +104,7 @@ fun AppTopBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderTopRouteBar(currentRoute, followLists, scaffoldState, accountViewModel)
|
RenderTopRouteBar(currentRoute, followLists, scaffoldState, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -111,16 +112,17 @@ private fun RenderTopRouteBar(
|
|||||||
currentRoute: String?,
|
currentRoute: String?,
|
||||||
followLists: FollowListViewModel,
|
followLists: FollowListViewModel,
|
||||||
scaffoldState: ScaffoldState,
|
scaffoldState: ScaffoldState,
|
||||||
accountViewModel: AccountViewModel
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
when (currentRoute) {
|
when (currentRoute) {
|
||||||
Route.Channel.base -> NoTopBar()
|
Route.Channel.base -> NoTopBar()
|
||||||
Route.Room.base -> NoTopBar()
|
Route.Room.base -> NoTopBar()
|
||||||
// Route.Profile.route -> TopBarWithBackButton(nav)
|
// Route.Profile.route -> TopBarWithBackButton(nav)
|
||||||
Route.Home.base -> HomeTopBar(followLists, scaffoldState, accountViewModel)
|
Route.Home.base -> HomeTopBar(followLists, scaffoldState, accountViewModel, nav)
|
||||||
Route.Video.base -> StoriesTopBar(followLists, scaffoldState, accountViewModel)
|
Route.Video.base -> StoriesTopBar(followLists, scaffoldState, accountViewModel, nav)
|
||||||
Route.Notification.base -> NotificationTopBar(followLists, scaffoldState, accountViewModel)
|
Route.Notification.base -> NotificationTopBar(followLists, scaffoldState, accountViewModel, nav)
|
||||||
else -> MainTopBar(scaffoldState, accountViewModel)
|
else -> MainTopBar(scaffoldState, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +131,8 @@ fun NoTopBar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StoriesTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
|
fun StoriesTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
GenericTopBar(scaffoldState, accountViewModel) { accountViewModel ->
|
GenericTopBar(scaffoldState, accountViewModel, nav) { accountViewModel ->
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
|
|
||||||
val list by remember(accountState) {
|
val list by remember(accountState) {
|
||||||
@@ -150,8 +152,8 @@ fun StoriesTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
|
fun HomeTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
GenericTopBar(scaffoldState, accountViewModel) { accountViewModel ->
|
GenericTopBar(scaffoldState, accountViewModel, nav) { accountViewModel ->
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
|
|
||||||
val list by remember(accountState) {
|
val list by remember(accountState) {
|
||||||
@@ -171,8 +173,8 @@ fun HomeTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NotificationTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
|
fun NotificationTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
GenericTopBar(scaffoldState, accountViewModel) { accountViewModel ->
|
GenericTopBar(scaffoldState, accountViewModel, nav) { accountViewModel ->
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
|
|
||||||
val list by remember(accountState) {
|
val list by remember(accountState) {
|
||||||
@@ -192,15 +194,15 @@ fun NotificationTopBar(followLists: FollowListViewModel, scaffoldState: Scaffold
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
|
fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
GenericTopBar(scaffoldState, accountViewModel) {
|
GenericTopBar(scaffoldState, accountViewModel, nav) {
|
||||||
AmethystIcon()
|
AmethystIcon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(coil.annotation.ExperimentalCoilApi::class)
|
@OptIn(coil.annotation.ExperimentalCoilApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, content: @Composable (AccountViewModel) -> Unit) {
|
fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit, content: @Composable (AccountViewModel) -> Unit) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
var wantsToEditRelays by remember {
|
var wantsToEditRelays by remember {
|
||||||
@@ -208,7 +210,7 @@ fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (wantsToEditRelays) {
|
if (wantsToEditRelays) {
|
||||||
NewRelayListView({ wantsToEditRelays = false }, accountViewModel)
|
NewRelayListView({ wantsToEditRelays = false }, accountViewModel, nav = nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(modifier = Modifier.height(50.dp)) {
|
Column(modifier = Modifier.height(50.dp)) {
|
||||||
|
|||||||
@@ -1199,7 +1199,7 @@ fun DisplayRelaySet(
|
|||||||
)
|
)
|
||||||
|
|
||||||
Column(modifier = Modifier.padding(start = 10.dp)) {
|
Column(modifier = Modifier.padding(start = 10.dp)) {
|
||||||
RelayOptionsAction(relay, accountViewModel)
|
RelayOptionsAction(relay, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1234,7 +1234,8 @@ fun DisplayRelaySet(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun RelayOptionsAction(
|
private fun RelayOptionsAction(
|
||||||
relay: String,
|
relay: String,
|
||||||
accountViewModel: AccountViewModel
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val userStateRelayInfo by accountViewModel.account.userProfile().live().relayInfo.observeAsState()
|
val userStateRelayInfo by accountViewModel.account.userProfile().live().relayInfo.observeAsState()
|
||||||
val isCurrentlyOnTheUsersList by remember(userStateRelayInfo) {
|
val isCurrentlyOnTheUsersList by remember(userStateRelayInfo) {
|
||||||
@@ -1248,7 +1249,7 @@ private fun RelayOptionsAction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (wantsToAddRelay.isNotEmpty()) {
|
if (wantsToAddRelay.isNotEmpty()) {
|
||||||
NewRelayListView({ wantsToAddRelay = "" }, accountViewModel, wantsToAddRelay)
|
NewRelayListView({ wantsToAddRelay = "" }, accountViewModel, wantsToAddRelay, nav = nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCurrentlyOnTheUsersList) {
|
if (isCurrentlyOnTheUsersList) {
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class RelayFeedViewModel : ViewModel() {
|
|||||||
fun RelayFeedView(
|
fun RelayFeedView(
|
||||||
viewModel: RelayFeedViewModel,
|
viewModel: RelayFeedViewModel,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit,
|
||||||
enablePullRefresh: Boolean = true
|
enablePullRefresh: Boolean = true
|
||||||
) {
|
) {
|
||||||
val feedState by viewModel.feedContent.collectAsState()
|
val feedState by viewModel.feedContent.collectAsState()
|
||||||
@@ -107,7 +108,7 @@ fun RelayFeedView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (wantsToAddRelay.isNotEmpty()) {
|
if (wantsToAddRelay.isNotEmpty()) {
|
||||||
NewRelayListView({ wantsToAddRelay = "" }, accountViewModel, wantsToAddRelay)
|
NewRelayListView({ wantsToAddRelay = "" }, accountViewModel, wantsToAddRelay, nav = nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
var refreshing by remember { mutableStateOf(false) }
|
var refreshing by remember { mutableStateOf(false) }
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
|
|||||||
AppBottomBar(accountViewModel, navState, navBottomRow)
|
AppBottomBar(accountViewModel, navState, navBottomRow)
|
||||||
},
|
},
|
||||||
topBar = {
|
topBar = {
|
||||||
AppTopBar(followLists, navState, scaffoldState, accountViewModel)
|
AppTopBar(followLists, navState, scaffoldState, accountViewModel, nav = nav)
|
||||||
},
|
},
|
||||||
drawerContent = {
|
drawerContent = {
|
||||||
DrawerContent(nav, scaffoldState, sheetState, accountViewModel)
|
DrawerContent(nav, scaffoldState, sheetState, accountViewModel)
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ private fun CreateAndRenderPages(
|
|||||||
4 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
4 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
||||||
5 -> TabBookmarks(baseUser, accountViewModel, nav)
|
5 -> TabBookmarks(baseUser, accountViewModel, nav)
|
||||||
6 -> TabReports(baseUser, accountViewModel, nav)
|
6 -> TabReports(baseUser, accountViewModel, nav)
|
||||||
7 -> TabRelays(baseUser, accountViewModel)
|
7 -> TabRelays(baseUser, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,7 +783,7 @@ private fun DrawAdditionalInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DisplayLNAddress(
|
fun DisplayLNAddress(
|
||||||
lud16: String?,
|
lud16: String?,
|
||||||
userHex: String,
|
userHex: String,
|
||||||
account: Account
|
account: Account
|
||||||
@@ -1294,7 +1294,7 @@ private fun WatchReportsAndUpdateFeed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabRelays(user: User, accountViewModel: AccountViewModel) {
|
fun TabRelays(user: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
val feedViewModel: RelayFeedViewModel = viewModel()
|
val feedViewModel: RelayFeedViewModel = viewModel()
|
||||||
|
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||||
@@ -1323,7 +1323,7 @@ fun TabRelays(user: User, accountViewModel: AccountViewModel) {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(vertical = 0.dp)
|
modifier = Modifier.padding(vertical = 0.dp)
|
||||||
) {
|
) {
|
||||||
RelayFeedView(feedViewModel, accountViewModel, enablePullRefresh = false)
|
RelayFeedView(feedViewModel, accountViewModel, enablePullRefresh = false, nav = nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -425,4 +425,27 @@
|
|||||||
<string name="sats_to_complete">Zapraiser at %1$s. %2$s sats to goal</string>
|
<string name="sats_to_complete">Zapraiser at %1$s. %2$s sats to goal</string>
|
||||||
<string name="read_from_relay">Read from Relay</string>
|
<string name="read_from_relay">Read from Relay</string>
|
||||||
<string name="write_to_relay">Write to Relay</string>
|
<string name="write_to_relay">Write to Relay</string>
|
||||||
|
<string name="an_error_ocurred_trying_to_get_relay_information">An error ocurred trying to get relay information</string>
|
||||||
|
<string name="owner">Owner</string>
|
||||||
|
<string name="version">Version</string>
|
||||||
|
<string name="software">Software</string>
|
||||||
|
<string name="contact">Contact</string>
|
||||||
|
<string name="supports">Supports</string>
|
||||||
|
<string name="admission_fees">Admission Fees</string>
|
||||||
|
<string name="payments_url">Payments url</string>
|
||||||
|
<string name="limitations">Limitations</string>
|
||||||
|
<string name="countries">Countries</string>
|
||||||
|
<string name="languages">Languages</string>
|
||||||
|
<string name="tags">Tags</string>
|
||||||
|
<string name="posting_policy">Posting policy</string>
|
||||||
|
<string name="message_length">Message length</string>
|
||||||
|
<string name="subscriptions">Subscriptions</string>
|
||||||
|
<string name="filters">Filters</string>
|
||||||
|
<string name="subscription_id_length">Subscription id length</string>
|
||||||
|
<string name="minimum_prefix">Minimum prefix</string>
|
||||||
|
<string name="maximum_event_tags">Maximum event tags</string>
|
||||||
|
<string name="content_length">Content length</string>
|
||||||
|
<string name="minimum_pow">Minimum PoW</string>
|
||||||
|
<string name="auth">Auth</string>
|
||||||
|
<string name="payment">Payment</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user