mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-29 11:02:35 +02:00
Adds individual relay information per chat message.
This commit is contained in:
@@ -227,7 +227,7 @@ class Account(
|
|||||||
privateKey = loggedIn.privKey!!
|
privateKey = loggedIn.privKey!!
|
||||||
)
|
)
|
||||||
Client.send(signedEvent)
|
Client.send(signedEvent)
|
||||||
LocalCache.consume(signedEvent)
|
LocalCache.consume(signedEvent, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendPrivateMeesage(message: String, toUser: String, replyingTo: Note? = null) {
|
fun sendPrivateMeesage(message: String, toUser: String, replyingTo: Note? = null) {
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ object LocalCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun consume(event: ChannelMessageEvent) {
|
fun consume(event: ChannelMessageEvent, relay: Relay?) {
|
||||||
if (event.channel.isNullOrBlank()) return
|
if (event.channel.isNullOrBlank()) return
|
||||||
|
|
||||||
val channel = getOrCreateChannel(event.channel)
|
val channel = getOrCreateChannel(event.channel)
|
||||||
@@ -356,6 +356,11 @@ object LocalCache {
|
|||||||
val note = getOrCreateNote(event.id.toHex())
|
val note = getOrCreateNote(event.id.toHex())
|
||||||
channel.addNote(note)
|
channel.addNote(note)
|
||||||
|
|
||||||
|
if (relay != null) {
|
||||||
|
note.author?.addRelay(relay, event.createdAt)
|
||||||
|
note.addRelay(relay)
|
||||||
|
}
|
||||||
|
|
||||||
// Already processed this event.
|
// Already processed this event.
|
||||||
if (note.event != null) return
|
if (note.event != null) return
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ abstract class NostrDataSource<T>(val debugName: String) {
|
|||||||
|
|
||||||
ChannelCreateEvent.kind -> LocalCache.consume(ChannelCreateEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
ChannelCreateEvent.kind -> LocalCache.consume(ChannelCreateEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
||||||
ChannelMetadataEvent.kind -> LocalCache.consume(ChannelMetadataEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
ChannelMetadataEvent.kind -> LocalCache.consume(ChannelMetadataEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
||||||
ChannelMessageEvent.kind -> LocalCache.consume(ChannelMessageEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
ChannelMessageEvent.kind -> LocalCache.consume(ChannelMessageEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig), relay)
|
||||||
ChannelHideMessageEvent.kind -> LocalCache.consume(ChannelHideMessageEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
ChannelHideMessageEvent.kind -> LocalCache.consume(ChannelHideMessageEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
||||||
ChannelMuteUserEvent.kind -> LocalCache.consume(ChannelMuteUserEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
ChannelMuteUserEvent.kind -> LocalCache.consume(ChannelMuteUserEvent(event.id, event.pubKey, event.createdAt, event.tags, event.content, event.sig))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.note
|
package com.vitorpamplona.amethyst.ui.note
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
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.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.LocalTextStyle
|
import androidx.compose.material.LocalTextStyle
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ChevronRight
|
||||||
|
import androidx.compose.material.icons.filled.ExpandMore
|
||||||
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
|
||||||
@@ -30,8 +39,11 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.graphics.ColorMatrix
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextDirection
|
import androidx.compose.ui.text.style.TextDirection
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -39,6 +51,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.compose.rememberAsyncImagePainter
|
import coil.compose.rememberAsyncImagePainter
|
||||||
|
import com.google.accompanist.flowlayout.FlowRow
|
||||||
import com.vitorpamplona.amethyst.NotificationCache
|
import com.vitorpamplona.amethyst.NotificationCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
||||||
@@ -235,6 +248,8 @@ fun ChatroomMessageCompose(baseNote: Note, routeForLastRead: String?, innerQuote
|
|||||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
|
||||||
fontSize = 12.sp
|
fontSize = 12.sp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
RelayBadges(note)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,3 +262,51 @@ fun ChatroomMessageCompose(baseNote: Note, routeForLastRead: String?, innerQuote
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RelayBadges(baseNote: Note) {
|
||||||
|
val noteRelaysState by baseNote.liveRelays.observeAsState()
|
||||||
|
val noteRelays = noteRelaysState?.note?.relays ?: emptySet()
|
||||||
|
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val relaysToDisplay = if (expanded) noteRelays else noteRelays.take(3)
|
||||||
|
|
||||||
|
val uri = LocalUriHandler.current
|
||||||
|
|
||||||
|
FlowRow(Modifier.padding(start = 10.dp)) {
|
||||||
|
relaysToDisplay.forEach {
|
||||||
|
val url = it.removePrefix("wss://")
|
||||||
|
Box(Modifier.size(15.dp).padding(1.dp)) {
|
||||||
|
AsyncImage(
|
||||||
|
model = "https://${url}/favicon.ico",
|
||||||
|
placeholder = rememberAsyncImagePainter("https://robohash.org/$url.png"),
|
||||||
|
fallback = rememberAsyncImagePainter("https://robohash.org/$url.png"),
|
||||||
|
error = rememberAsyncImagePainter("https://robohash.org/$url.png"),
|
||||||
|
contentDescription = "Relay Icon",
|
||||||
|
colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(1f)
|
||||||
|
.clip(shape = CircleShape)
|
||||||
|
.background(MaterialTheme.colors.background)
|
||||||
|
.clickable(onClick = { uri.openUri("https://" + url) } )
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noteRelays.size > 3 && !expanded) {
|
||||||
|
IconButton(
|
||||||
|
modifier = Modifier.then(Modifier.size(15.dp)),
|
||||||
|
onClick = { expanded = true }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.ChevronRight,
|
||||||
|
null,
|
||||||
|
modifier = Modifier.size(15.dp),
|
||||||
|
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,34 +2,18 @@ package com.vitorpamplona.amethyst.ui.screen
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.security.crypto.EncryptedSharedPreferences
|
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.ServiceManager
|
import com.vitorpamplona.amethyst.ServiceManager
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.DefaultChannels
|
|
||||||
import com.vitorpamplona.amethyst.model.toByteArray
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrGlobalDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrNotificationDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.relays.Client
|
|
||||||
import com.vitorpamplona.amethyst.ui.MainActivity
|
|
||||||
import fr.acinq.secp256k1.Hex
|
import fr.acinq.secp256k1.Hex
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import nostr.postr.Persona
|
import nostr.postr.Persona
|
||||||
import nostr.postr.bechToBytes
|
import nostr.postr.bechToBytes
|
||||||
import nostr.postr.toHex
|
|
||||||
|
|
||||||
class AccountStateViewModel(private val localPreferences: LocalPreferences): ViewModel() {
|
class AccountStateViewModel(private val localPreferences: LocalPreferences): ViewModel() {
|
||||||
private val _accountContent = MutableStateFlow<AccountState>(AccountState.LoggedOff)
|
private val _accountContent = MutableStateFlow<AccountState>(AccountState.LoggedOff)
|
||||||
|
|||||||
Reference in New Issue
Block a user