app meta data, show content dvms
This commit is contained in:
@@ -28,7 +28,6 @@ import com.vitorpamplona.quartz.events.MuteListEvent
|
|||||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||||
|
|
||||||
open class DiscoverNIP89FeedFilter(
|
open class DiscoverNIP89FeedFilter(
|
||||||
val ktag: Int,
|
|
||||||
val account: Account,
|
val account: Account,
|
||||||
) : AdditiveFeedFilter<Note>() {
|
) : AdditiveFeedFilter<Note>() {
|
||||||
override fun feedKey(): String {
|
override fun feedKey(): String {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
|
import com.vitorpamplona.quartz.events.AppDefinitionEvent
|
||||||
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
||||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||||
@@ -213,6 +214,9 @@ fun InnerChannelCardWithReactions(
|
|||||||
is ClassifiedsEvent -> {
|
is ClassifiedsEvent -> {
|
||||||
InnerCardBox(baseNote, accountViewModel, nav)
|
InnerCardBox(baseNote, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
is AppDefinitionEvent -> {
|
||||||
|
InnerCardRow(baseNote, accountViewModel, nav)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +272,9 @@ private fun RenderNoteRow(
|
|||||||
is ChannelCreateEvent -> {
|
is ChannelCreateEvent -> {
|
||||||
RenderChannelThumb(baseNote, accountViewModel, nav)
|
RenderChannelThumb(baseNote, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
is AppDefinitionEvent -> {
|
||||||
|
RenderContentDVMThumb(baseNote, accountViewModel, nav)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,6 +523,14 @@ data class CommunityCard(
|
|||||||
val moderators: ImmutableList<Participant>,
|
val moderators: ImmutableList<Participant>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class DVMCard(
|
||||||
|
val name: String,
|
||||||
|
val description: String?,
|
||||||
|
val cover: String?,
|
||||||
|
val moderators: ImmutableList<Participant>,
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RenderCommunitiesThumb(
|
fun RenderCommunitiesThumb(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
@@ -715,6 +730,94 @@ private fun LoadParticipants(
|
|||||||
inner(participantUsers)
|
inner(participantUsers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RenderContentDVMThumb(
|
||||||
|
baseNote: Note,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val noteEvent = baseNote.event as? AppDefinitionEvent ?: return
|
||||||
|
|
||||||
|
val card by
|
||||||
|
baseNote
|
||||||
|
.live()
|
||||||
|
.metadata
|
||||||
|
.map {
|
||||||
|
val noteEvent = it.note.event as? AppDefinitionEvent
|
||||||
|
|
||||||
|
DVMCard(
|
||||||
|
name = noteEvent?.appMetaData()?.name ?: "",
|
||||||
|
description = noteEvent?.appMetaData()?.about ?: "",
|
||||||
|
cover = noteEvent?.appMetaData()?.image?.ifBlank { null },
|
||||||
|
moderators = persistentListOf(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.observeAsState(
|
||||||
|
DVMCard(
|
||||||
|
name = noteEvent.appMetaData()?.name ?: "",
|
||||||
|
description = noteEvent.appMetaData()?.about ?: "",
|
||||||
|
cover = noteEvent.appMetaData()?.image?.ifBlank { null },
|
||||||
|
moderators = persistentListOf(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
LeftPictureLayout(
|
||||||
|
onImage = {
|
||||||
|
card.cover?.let {
|
||||||
|
Box(contentAlignment = BottomStart) {
|
||||||
|
AsyncImage(
|
||||||
|
model = it,
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.fillMaxSize().clip(QuoteBorder),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: run { DisplayAuthorBanner(baseNote) }
|
||||||
|
},
|
||||||
|
onTitleRow = {
|
||||||
|
Text(
|
||||||
|
text = card.name,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
LikeReaction(
|
||||||
|
baseNote = baseNote,
|
||||||
|
grayTint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav,
|
||||||
|
)
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
ZapReaction(
|
||||||
|
baseNote = baseNote,
|
||||||
|
grayTint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onDescription = {
|
||||||
|
card.description?.let {
|
||||||
|
Spacer(modifier = StdVertSpacer)
|
||||||
|
Row {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
color = MaterialTheme.colorScheme.placeholderText,
|
||||||
|
maxLines = 3,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onBottomRow = {
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RenderChannelThumb(
|
fun RenderChannelThumb(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
|
|||||||
@@ -234,3 +234,25 @@ fun LoadChannel(
|
|||||||
|
|
||||||
channel?.let { content(it) }
|
channel?.let { content(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoadDVMNip89(
|
||||||
|
baseChannelHex: String,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
content: @Composable (Channel) -> Unit,
|
||||||
|
) {
|
||||||
|
var channel by
|
||||||
|
remember(baseChannelHex) {
|
||||||
|
mutableStateOf<Channel?>(accountViewModel.getChannelIfExists(baseChannelHex))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel == null) {
|
||||||
|
LaunchedEffect(key1 = baseChannelHex) {
|
||||||
|
accountViewModel.checkGetOrCreateChannel(baseChannelHex) { newChannel ->
|
||||||
|
launch(Dispatchers.Main) { channel = newChannel }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channel?.let { content(it) }
|
||||||
|
}
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ import com.vitorpamplona.amethyst.ui.theme.Size16Modifier
|
|||||||
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 com.vitorpamplona.quartz.events.AppDefinitionEvent
|
import com.vitorpamplona.quartz.events.AppDefinitionEvent
|
||||||
|
import com.vitorpamplona.quartz.events.AppMetadata
|
||||||
import com.vitorpamplona.quartz.events.EmptyTagList
|
import com.vitorpamplona.quartz.events.EmptyTagList
|
||||||
import com.vitorpamplona.quartz.events.UserMetadata
|
|
||||||
import com.vitorpamplona.quartz.events.toImmutableListOfLists
|
import com.vitorpamplona.quartz.events.toImmutableListOfLists
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -85,7 +85,7 @@ fun RenderAppDefinition(
|
|||||||
) {
|
) {
|
||||||
val noteEvent = note.event as? AppDefinitionEvent ?: return
|
val noteEvent = note.event as? AppDefinitionEvent ?: return
|
||||||
|
|
||||||
var metadata by remember { mutableStateOf<UserMetadata?>(null) }
|
var metadata by remember { mutableStateOf<AppMetadata?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(key1 = noteEvent) {
|
LaunchedEffect(key1 = noteEvent) {
|
||||||
withContext(Dispatchers.Default) { metadata = noteEvent.appMetaData() }
|
withContext(Dispatchers.Default) { metadata = noteEvent.appMetaData() }
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class NostrDiscoverMarketplaceFeedViewModel(val account: Account) :
|
|||||||
|
|
||||||
class NostrDiscoverNIP89FeedViewModel(val account: Account) :
|
class NostrDiscoverNIP89FeedViewModel(val account: Account) :
|
||||||
FeedViewModel(
|
FeedViewModel(
|
||||||
DiscoverNIP89FeedFilter(5003, account),
|
DiscoverNIP89FeedFilter(account),
|
||||||
) {
|
) {
|
||||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||||
override fun <NostrDiscoverNIP89FeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverNIP89FeedViewModel>): NostrDiscoverNIP89FeedViewModel {
|
override fun <NostrDiscoverNIP89FeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverNIP89FeedViewModel>): NostrDiscoverNIP89FeedViewModel {
|
||||||
|
|||||||
@@ -22,11 +22,94 @@ package com.vitorpamplona.quartz.events
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class AppMetadata {
|
||||||
|
var name: String? = null
|
||||||
|
var username: String? = null
|
||||||
|
|
||||||
|
@JsonProperty("display_name")
|
||||||
|
var displayName: String? = null
|
||||||
|
var picture: String? = null
|
||||||
|
|
||||||
|
var banner: String? = null
|
||||||
|
var image: String? = null
|
||||||
|
var website: String? = null
|
||||||
|
var about: String? = null
|
||||||
|
var subscription: Boolean? = false
|
||||||
|
var cashuAccepted: Boolean? = false
|
||||||
|
var encryptionSupported: Boolean? = false
|
||||||
|
var personalized: Boolean? = false
|
||||||
|
var amount: String? = null
|
||||||
|
|
||||||
|
var nip05: String? = null
|
||||||
|
var domain: String? = null
|
||||||
|
var lud06: String? = null
|
||||||
|
var lud16: String? = null
|
||||||
|
|
||||||
|
var twitter: String? = null
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var tags: ImmutableListOfLists<String>? = null
|
||||||
|
|
||||||
|
fun anyName(): String? {
|
||||||
|
return displayName ?: name ?: username
|
||||||
|
}
|
||||||
|
|
||||||
|
fun anyNameStartsWith(prefix: String): Boolean {
|
||||||
|
return listOfNotNull(name, username, displayName, nip05, lud06, lud16).any {
|
||||||
|
it.contains(prefix, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lnAddress(): String? {
|
||||||
|
return lud16 ?: lud06
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bestName(): String? {
|
||||||
|
return displayName ?: name ?: username
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nip05(): String? {
|
||||||
|
return nip05
|
||||||
|
}
|
||||||
|
|
||||||
|
fun profilePicture(): String? {
|
||||||
|
return picture
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanBlankNames() {
|
||||||
|
if (picture?.isNotEmpty() == true) picture = picture?.trim()
|
||||||
|
if (nip05?.isNotEmpty() == true) nip05 = nip05?.trim()
|
||||||
|
|
||||||
|
if (displayName?.isNotEmpty() == true) displayName = displayName?.trim()
|
||||||
|
if (name?.isNotEmpty() == true) name = name?.trim()
|
||||||
|
if (username?.isNotEmpty() == true) username = username?.trim()
|
||||||
|
if (lud06?.isNotEmpty() == true) lud06 = lud06?.trim()
|
||||||
|
if (lud16?.isNotEmpty() == true) lud16 = lud16?.trim()
|
||||||
|
|
||||||
|
if (website?.isNotEmpty() == true) website = website?.trim()
|
||||||
|
if (domain?.isNotEmpty() == true) domain = domain?.trim()
|
||||||
|
|
||||||
|
if (picture?.isBlank() == true) picture = null
|
||||||
|
if (nip05?.isBlank() == true) nip05 = null
|
||||||
|
if (displayName?.isBlank() == true) displayName = null
|
||||||
|
if (name?.isBlank() == true) name = null
|
||||||
|
if (username?.isBlank() == true) username = null
|
||||||
|
if (lud06?.isBlank() == true) lud06 = null
|
||||||
|
if (lud16?.isBlank() == true) lud16 = null
|
||||||
|
|
||||||
|
if (website?.isBlank() == true) website = null
|
||||||
|
if (domain?.isBlank() == true) domain = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class AppDefinitionEvent(
|
class AppDefinitionEvent(
|
||||||
id: HexKey,
|
id: HexKey,
|
||||||
@@ -36,7 +119,7 @@ class AppDefinitionEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
@Transient private var cachedMetadata: UserMetadata? = null
|
@Transient private var cachedMetadata: AppMetadata? = null
|
||||||
|
|
||||||
fun appMetaData() =
|
fun appMetaData() =
|
||||||
if (cachedMetadata != null) {
|
if (cachedMetadata != null) {
|
||||||
@@ -46,7 +129,7 @@ class AppDefinitionEvent(
|
|||||||
val newMetadata =
|
val newMetadata =
|
||||||
mapper.readValue(
|
mapper.readValue(
|
||||||
ByteArrayInputStream(content.toByteArray(Charsets.UTF_8)),
|
ByteArrayInputStream(content.toByteArray(Charsets.UTF_8)),
|
||||||
UserMetadata::class.java,
|
AppMetadata::class.java,
|
||||||
)
|
)
|
||||||
|
|
||||||
cachedMetadata = newMetadata
|
cachedMetadata = newMetadata
|
||||||
|
|||||||
Reference in New Issue
Block a user