Sending confirmation events back to the Repository.

This commit is contained in:
Vitor Pamplona
2023-01-13 21:35:28 -05:00
parent 353046b451
commit d130a43358
9 changed files with 54 additions and 17 deletions

View File

@@ -44,6 +44,12 @@ class Account(val loggedIn: Persona) {
} }
} }
fun broadcast(note: Note) {
note.event?.let {
Client.send(it)
}
}
fun sendPost(message: String, replyingTo: Note?) { fun sendPost(message: String, replyingTo: Note?) {
if (!isWriteable()) return if (!isWriteable()) return

View File

@@ -57,6 +57,10 @@ abstract class NostrDataSource(val debugName: String) {
resetFilters() resetFilters()
}*/ }*/
} }
override fun onSendResponse(eventId: String, success: Boolean, message: String, relay: Relay) {
}
} }
init { init {

View File

@@ -85,6 +85,10 @@ object Client: RelayPool.Listener {
listeners.forEach { it.onRelayStateChange(type, relay) } listeners.forEach { it.onRelayStateChange(type, relay) }
} }
override fun onSendResponse(eventId: String, success: Boolean, message: String, relay: Relay) {
listeners.forEach { it.onSendResponse(eventId, success, message, relay) }
}
fun subscribe(listener: Listener) { fun subscribe(listener: Listener) {
listeners.add(listener) listeners.add(listener)
} }
@@ -109,5 +113,10 @@ object Client: RelayPool.Listener {
* Connected to or disconnected from a relay * Connected to or disconnected from a relay
*/ */
open fun onRelayStateChange(type: Relay.Type, relay: Relay) = Unit open fun onRelayStateChange(type: Relay.Type, relay: Relay) = Unit
/**
* When an relay saves or rejects a new event.
*/
open fun onSendResponse(eventId: String, success: Boolean, message: String, relay: Relay) = Unit
} }
} }

View File

@@ -59,10 +59,10 @@ class Relay(
it.onError(this@Relay, channel, Error("Relay sent notice: $channel")) it.onError(this@Relay, channel, Error("Relay sent notice: $channel"))
} }
"OK" -> listeners.forEach { "OK" -> listeners.forEach {
// "channel" being the second string in the string array ... it.onSendResponse(this@Relay, msg[1].asString, msg[2].asBoolean, msg[3].asString)
// Event was saved correctly?
} }
else -> listeners.forEach { else -> listeners.forEach {
println("else: " + text)
it.onError( it.onError(
this@Relay, this@Relay,
channel, channel,
@@ -155,6 +155,7 @@ class Relay(
fun onError(relay: Relay, subscriptionId: String, error: Error) fun onError(relay: Relay, subscriptionId: String, error: Error)
fun onSendResponse(relay: Relay, eventId: String, success: Boolean, message: String)
/** /**
* Connected to or disconnected from a relay * Connected to or disconnected from a relay
* *

View File

@@ -12,9 +12,12 @@ object RelayPool: Relay.Listener {
private val relays = Collections.synchronizedList(ArrayList<Relay>()) private val relays = Collections.synchronizedList(ArrayList<Relay>())
private val listeners = Collections.synchronizedSet(HashSet<Listener>()) private val listeners = Collections.synchronizedSet(HashSet<Listener>())
fun report(): String { fun availableRelays(): Int {
val connected = relays.filter { it.isConnected() } return relays.size
return "${connected.size}/${relays.size}" }
fun connectedRelays(): Int {
return relays.filter { it.isConnected() }.size
} }
fun loadRelays(relayList: List<Relay>? = null){ fun loadRelays(relayList: List<Relay>? = null){
@@ -79,6 +82,8 @@ object RelayPool: Relay.Listener {
fun onError(error: Error, subscriptionId: String, relay: Relay) fun onError(error: Error, subscriptionId: String, relay: Relay)
fun onRelayStateChange(type: Relay.Type, relay: Relay) fun onRelayStateChange(type: Relay.Type, relay: Relay)
fun onSendResponse(eventId: String, success: Boolean, message: String, relay: Relay)
} }
@Synchronized @Synchronized
@@ -96,6 +101,10 @@ object RelayPool: Relay.Listener {
refreshObservers() refreshObservers()
} }
override fun onSendResponse(relay: Relay, eventId: String, success: Boolean, message: String) {
listeners.forEach { it.onSendResponse(eventId, success, message, relay) }
}
// Observers line up here. // Observers line up here.
val live: RelayPoolLiveData = RelayPoolLiveData(this) val live: RelayPoolLiveData = RelayPoolLiveData(this)

View File

@@ -1,18 +1,26 @@
package com.vitorpamplona.amethyst.ui.components package com.vitorpamplona.amethyst.ui.components
import android.util.Patterns import android.util.Patterns
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
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.toNote
import com.vitorpamplona.amethyst.ui.note.toDisplayHex
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URISyntaxException import java.net.URISyntaxException
@@ -22,7 +30,7 @@ import java.util.regex.Pattern
val imageExtension = Pattern.compile("(.*/)*.+\\.(png|jpg|gif|bmp|jpeg|webp)$") val imageExtension = Pattern.compile("(.*/)*.+\\.(png|jpg|gif|bmp|jpeg|webp)$")
val videoExtension = Pattern.compile("(.*/)*.+\\.(mp4|avi|wmv|mpg|amv|webm)$") val videoExtension = Pattern.compile("(.*/)*.+\\.(mp4|avi|wmv|mpg|amv|webm)$")
val noProtocolUrlValidator = Pattern.compile("^[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&//=]*)$") val noProtocolUrlValidator = Pattern.compile("^[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&//=]*)$")
val tagIndex = Pattern.compile("\\#\\[([0-9]*)\\]") val tagIndex = Pattern.compile(".*\\#\\[([0-9]+)\\].*")
val mentionsPattern: Pattern = Pattern.compile("@([A-Za-z0-9_-]+)") val mentionsPattern: Pattern = Pattern.compile("@([A-Za-z0-9_-]+)")
val hashTagsPattern: Pattern = Pattern.compile("#([A-Za-z0-9_-]+)") val hashTagsPattern: Pattern = Pattern.compile("#([A-Za-z0-9_-]+)")
@@ -41,7 +49,7 @@ fun isValidURL(url: String?): Boolean {
@OptIn(ExperimentalComposeUiApi::class) @OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
fun RichTextViewer(content: String, tags: List<List<String>>?, note: Note, accountViewModel: AccountViewModel) { fun RichTextViewer(content: String, tags: List<List<String>>?, note: Note, accountViewModel: AccountViewModel, navController: NavController) {
Column(modifier = Modifier.padding(top = 5.dp)) { Column(modifier = Modifier.padding(top = 5.dp)) {
// FlowRow doesn't work well with paragraphs. So we need to split them // FlowRow doesn't work well with paragraphs. So we need to split them
content.split('\n').forEach { paragraph -> content.split('\n').forEach { paragraph ->
@@ -64,7 +72,7 @@ fun RichTextViewer(content: String, tags: List<List<String>>?, note: Note, accou
} else if (noProtocolUrlValidator.matcher(word).matches()) { } else if (noProtocolUrlValidator.matcher(word).matches()) {
UrlPreview("https://$word", word) UrlPreview("https://$word", word)
} else if (tagIndex.matcher(word).matches() && tags != null) { } else if (tagIndex.matcher(word).matches() && tags != null) {
TagLink(word, tags) TagLink(word, tags, navController)
} else { } else {
Text(text = "$word ") Text(text = "$word ")
} }
@@ -76,7 +84,7 @@ fun RichTextViewer(content: String, tags: List<List<String>>?, note: Note, accou
} }
@Composable @Composable
fun TagLink(word: String, tags: List<List<String>>) { fun TagLink(word: String, tags: List<List<String>>, navController: NavController) {
val matcher = tagIndex.matcher(word) val matcher = tagIndex.matcher(word)
val index = try { val index = try {
@@ -97,15 +105,17 @@ fun TagLink(word: String, tags: List<List<String>>) {
if (user != null) { if (user != null) {
val innerUserState by user.live.observeAsState() val innerUserState by user.live.observeAsState()
Text( Text(
"${innerUserState?.user?.toBestDisplayName()}" "@${innerUserState?.user?.toBestDisplayName()} "
) )
} }
} else if (tags[index][0] == "e") { } else if (tags[index][0] == "e") {
val note = LocalCache.notes[tags[index][1]] val note = LocalCache.notes[tags[index][1]]
if (note != null) { if (note != null) {
val innerNoteState by note.live.observeAsState() val innerNoteState by note.live.observeAsState()
Text( ClickableText(
"${innerNoteState?.note?.idDisplayHex}" text = AnnotatedString("@${innerNoteState?.note?.id?.toNote()?.toDisplayHex()} "),
onClick = { navController.navigate("Note/${note.idHex}") },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
) )
} }
} else } else

View File

@@ -145,7 +145,7 @@ fun NoteCompose(baseNote: Note, modifier: Modifier = Modifier, isInnerNote: Bool
} else { } else {
val eventContent = note.event?.content val eventContent = note.event?.content
if (eventContent != null) if (eventContent != null)
RichTextViewer(eventContent, note.event?.tags, note, accountViewModel) RichTextViewer(eventContent, note.event?.tags, note, accountViewModel, navController)
ReactionsRowState(note, accountViewModel) ReactionsRowState(note, accountViewModel)

View File

@@ -213,7 +213,7 @@ fun NoteMaster(baseNote: Note, accountViewModel: AccountViewModel, navController
Column() { Column() {
val eventContent = note.event?.content val eventContent = note.event?.content
if (eventContent != null) if (eventContent != null)
RichTextViewer(eventContent, note.event?.tags, note, accountViewModel) RichTextViewer(eventContent, note.event?.tags, note, accountViewModel, navController)
ReactionsRowState(note, accountViewModel) ReactionsRowState(note, accountViewModel)

View File

@@ -22,8 +22,6 @@ class AccountViewModel(private val account: Account): ViewModel() {
} }
fun broadcast(note: Note) { fun broadcast(note: Note) {
note.event?.let { account.broadcast(note)
Client.send(it)
}
} }
} }