mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-03-26 17:52:29 +01:00
add NIP90 status events, update NIP90 feeds based on request id
This commit is contained in:
parent
f09b00ff01
commit
142aca40ce
@ -91,6 +91,7 @@ import com.vitorpamplona.quartz.events.MetadataEvent
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90ContentDiscoveryRequestEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90StatusEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90UserDiscoveryRequestEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90UserDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.events.NNSEvent
|
||||
@ -444,6 +445,34 @@ object LocalCache {
|
||||
refreshObservers(note)
|
||||
}
|
||||
|
||||
fun consume(
|
||||
event: NIP90StatusEvent,
|
||||
relay: Relay? = null,
|
||||
) {
|
||||
val note = getOrCreateNote(event.id)
|
||||
val author = getOrCreateUser(event.pubKey)
|
||||
|
||||
if (relay != null) {
|
||||
author.addRelayBeingUsed(relay, event.createdAt)
|
||||
note.addRelay(relay)
|
||||
}
|
||||
|
||||
// Already processed this event.
|
||||
if (note.event != null) return
|
||||
|
||||
val replyTo = computeReplyTo(event)
|
||||
|
||||
note.loadEvent(event, author, replyTo)
|
||||
|
||||
// Log.d("TN", "New Note (${notes.size},${users.size}) ${note.author?.toBestDisplayName()}
|
||||
// ${note.event?.content()?.split("\n")?.take(100)} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
// Counts the replies
|
||||
replyTo.forEach { it.addReply(note) }
|
||||
|
||||
refreshObservers(note)
|
||||
}
|
||||
|
||||
fun consume(
|
||||
event: NIP90UserDiscoveryResponseEvent,
|
||||
relay: Relay? = null,
|
||||
@ -2415,6 +2444,7 @@ object LocalCache {
|
||||
}
|
||||
}
|
||||
is LnZapRequestEvent -> consume(event)
|
||||
is NIP90StatusEvent -> consume(event, relay)
|
||||
is NIP90ContentDiscoveryResponseEvent -> consume(event, relay)
|
||||
is NIP90ContentDiscoveryRequestEvent -> consume(event, relay)
|
||||
is NIP90UserDiscoveryResponseEvent -> consume(event, relay)
|
||||
|
@ -159,7 +159,25 @@ object NostrDiscoveryDataSource : NostrDataSource("DiscoveryFeed") {
|
||||
types = setOf(FeedType.GLOBAL),
|
||||
filter =
|
||||
JsonFilter(
|
||||
kinds = listOf(NIP90ContentDiscoveryResponseEvent.KIND, NIP90StatusEvent.KIND),
|
||||
kinds = listOf(NIP90ContentDiscoveryResponseEvent.KIND),
|
||||
limit = 300,
|
||||
since =
|
||||
latestEOSEs.users[account.userProfile()]
|
||||
?.followList
|
||||
?.get(account.defaultDiscoveryFollowList.value)
|
||||
?.relayList,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createNIP90StatusFilter(): List<TypedFilter> {
|
||||
return listOfNotNull(
|
||||
TypedFilter(
|
||||
types = setOf(FeedType.GLOBAL),
|
||||
filter =
|
||||
JsonFilter(
|
||||
kinds = listOf(NIP90StatusEvent.KIND),
|
||||
limit = 300,
|
||||
since =
|
||||
latestEOSEs.users[account.userProfile()]
|
||||
@ -446,6 +464,7 @@ object NostrDiscoveryDataSource : NostrDataSource("DiscoveryFeed") {
|
||||
createLiveStreamFilter()
|
||||
.plus(createNIP89Filter(listOf("5300")))
|
||||
.plus(createNIP90ResponseFilter())
|
||||
.plus(createNIP90StatusFilter())
|
||||
.plus(createPublicChatFilter())
|
||||
.plus(createMarketplaceFilter())
|
||||
.plus(
|
||||
|
@ -26,10 +26,14 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.events.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
open class DiscoverNIP89FeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
val lastAnnounced = 90 * 24 * 60 * 60 // 90 Days ago
|
||||
// TODO better than announced would be last active, as this requires the DVM provider to regularly update the NIP89 announcement
|
||||
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + followList()
|
||||
}
|
||||
@ -49,7 +53,7 @@ open class DiscoverNIP89FeedFilter(
|
||||
val notes =
|
||||
LocalCache.addressables.filterIntoSet { _, it ->
|
||||
val noteEvent = it.event
|
||||
noteEvent is AppDefinitionEvent // && params.match(noteEvent)
|
||||
noteEvent is AppDefinitionEvent && noteEvent.createdAt > TimeUtils.now() - lastAnnounced // && params.match(noteEvent)
|
||||
}
|
||||
|
||||
return sort(notes)
|
||||
@ -73,7 +77,7 @@ open class DiscoverNIP89FeedFilter(
|
||||
|
||||
return collection.filterTo(HashSet()) {
|
||||
val noteEvent = it.event
|
||||
noteEvent is AppDefinitionEvent // && params.match(noteEvent)
|
||||
noteEvent is AppDefinitionEvent && noteEvent.createdAt > TimeUtils.now() - lastAnnounced // && params.match(noteEvent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
@ -33,9 +32,10 @@ import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
open class NIP90ContentDiscoveryFilter(
|
||||
val account: Account,
|
||||
val dvmkey: String,
|
||||
val request: String,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + followList()
|
||||
return account.userProfile().pubkeyHex + "-" + request
|
||||
}
|
||||
|
||||
open fun followList(): String {
|
||||
@ -53,8 +53,10 @@ open class NIP90ContentDiscoveryFilter(
|
||||
val notes =
|
||||
LocalCache.notes.filterIntoSet { _, it ->
|
||||
val noteEvent = it.event
|
||||
noteEvent is NIP90ContentDiscoveryResponseEvent && it.event?.pubKey() == dvmkey && it.event?.isTaggedUser(account.keyPair.pubKey.toHexKey()) == true // && params.match(noteEvent)
|
||||
noteEvent is NIP90ContentDiscoveryResponseEvent && it.event!!.isTaggedEvent(request)
|
||||
// it.event?.pubKey() == dvmkey && it.event?.isTaggedUser(account.keyPair.pubKey.toHexKey()) == true // && params.match(noteEvent)
|
||||
}
|
||||
|
||||
var sorted = sort(notes)
|
||||
if (sorted.isNotEmpty()) {
|
||||
var note = sorted.first()
|
||||
@ -99,16 +101,17 @@ open class NIP90ContentDiscoveryFilter(
|
||||
val notes =
|
||||
collection.filterTo(HashSet()) {
|
||||
val noteEvent = it.event
|
||||
noteEvent is NIP90ContentDiscoveryResponseEvent && it.event?.isTaggedUser(account.keyPair.pubKey.toHexKey()) == true // && params.match(noteEvent)
|
||||
noteEvent is NIP90ContentDiscoveryResponseEvent && // &&
|
||||
it.event!!.isTaggedEvent(request) // && it.event?.isTaggedUser(account.keyPair.pubKey.toHexKey()) == true // && params.match(noteEvent)
|
||||
}
|
||||
|
||||
// TODO. We want to parse the content of the latest event to ids and get the nodes from these ids
|
||||
|
||||
var sorted = sort(notes)
|
||||
val sorted = sort(notes)
|
||||
println("REQUEST: " + request)
|
||||
if (sorted.isNotEmpty()) {
|
||||
var note = sorted.first()
|
||||
|
||||
var eventContent = note.event?.content()
|
||||
println(eventContent)
|
||||
|
||||
val collection: HashSet<Note> = hashSetOf()
|
||||
val mapper = jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
@ -118,7 +121,7 @@ open class NIP90ContentDiscoveryFilter(
|
||||
// TODO. This is ugly. how to Kotlin?
|
||||
var id = element.toString().trimStart('[').trimStart('e').trimStart(',').trimEnd(']').trimStart().trimEnd()
|
||||
|
||||
var note = LocalCache.checkGetOrCreateNote(id)
|
||||
val note = LocalCache.checkGetOrCreateNote(id)
|
||||
if (note != null) {
|
||||
collection.add(note)
|
||||
}
|
||||
@ -127,8 +130,6 @@ open class NIP90ContentDiscoveryFilter(
|
||||
} else {
|
||||
return notes
|
||||
}
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
|
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90StatusEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
|
||||
open class NIP90StatusFilter(
|
||||
val account: Account,
|
||||
val dvmkey: String,
|
||||
val request: String,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + followList()
|
||||
}
|
||||
|
||||
open fun followList(): String {
|
||||
return account.defaultDiscoveryFollowList.value
|
||||
}
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
followList() == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
|
||||
val status =
|
||||
LocalCache.notes.filterIntoSet { _, it ->
|
||||
val noteEvent = it.event
|
||||
noteEvent is NIP90StatusEvent && it.event?.pubKey() == dvmkey &&
|
||||
it.event!!.isTaggedEvent(request)
|
||||
// && it.event?.isTaggedUser(account.keyPair.pubKey.toHexKey()) == true // && params.match(noteEvent)
|
||||
}
|
||||
if (status.isNotEmpty()) {
|
||||
println("Found status")
|
||||
return listOf(status.first())
|
||||
} else {
|
||||
println("Empty status")
|
||||
return listOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
account.userProfile().pubkeyHex,
|
||||
account.defaultDiscoveryFollowList.value,
|
||||
account.liveDiscoveryFollowLists.value,
|
||||
account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
// val params = buildFilterParams(account)
|
||||
|
||||
val status =
|
||||
LocalCache.notes.filterIntoSet { _, it ->
|
||||
val noteEvent = it.event
|
||||
noteEvent is NIP90StatusEvent && it.event?.pubKey() == dvmkey &&
|
||||
it.event!!.isTaggedEvent(request)
|
||||
// && it.event?.isTaggedUser(account.keyPair.pubKey.toHexKey()) == true // && params.match(noteEvent)
|
||||
}
|
||||
if (status.isNotEmpty()) {
|
||||
println("Found status")
|
||||
return setOf(status.first())
|
||||
} else {
|
||||
println("Empty status")
|
||||
return setOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.toList() // collection.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
||||
}
|
||||
}
|
@ -100,6 +100,7 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderLiveActivityChatMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLiveActivityEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLongFormContent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNIP90ContentDiscoveryResponse
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNIP90Status
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPinListEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPoll
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPostApproval
|
||||
@ -163,6 +164,7 @@ import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.events.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.events.NIP90StatusEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import com.vitorpamplona.quartz.events.PinListEvent
|
||||
import com.vitorpamplona.quartz.events.PollNoteEvent
|
||||
@ -683,6 +685,19 @@ private fun RenderNoteRow(
|
||||
nav,
|
||||
)
|
||||
|
||||
is NIP90StatusEvent ->
|
||||
RenderNIP90Status(
|
||||
baseNote,
|
||||
makeItShort,
|
||||
canPreview,
|
||||
quotesLeft,
|
||||
unPackReply,
|
||||
backgroundColor,
|
||||
editState,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
|
||||
is PollNoteEvent -> {
|
||||
RenderPoll(
|
||||
baseNote,
|
||||
|
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note.types
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadDecryptedContent
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.events.BaseTextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
|
||||
@Composable
|
||||
fun RenderNIP90Status(
|
||||
note: Note,
|
||||
makeItShort: Boolean,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
unPackReply: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
val noteEvent = note.event
|
||||
val modifier = remember(note) { Modifier.fillMaxWidth() }
|
||||
|
||||
val showReply by
|
||||
remember(note) {
|
||||
derivedStateOf {
|
||||
noteEvent is BaseTextNoteEvent && !makeItShort && unPackReply && (note.replyTo != null || noteEvent.hasAnyTaggedUser())
|
||||
}
|
||||
}
|
||||
|
||||
if (showReply) {
|
||||
val replyingDirectlyTo =
|
||||
remember(note) {
|
||||
if (noteEvent is BaseTextNoteEvent) {
|
||||
val replyingTo = noteEvent.replyingToAddressOrEvent()
|
||||
if (replyingTo != null) {
|
||||
val newNote = accountViewModel.getNoteIfExists(replyingTo)
|
||||
if (newNote != null && newNote.channelHex() == null && newNote.event?.kind() != CommunityDefinitionEvent.KIND) {
|
||||
newNote
|
||||
} else {
|
||||
note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.KIND }
|
||||
}
|
||||
} else {
|
||||
note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.KIND }
|
||||
}
|
||||
} else {
|
||||
note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.KIND }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoadDecryptedContent(
|
||||
note,
|
||||
accountViewModel,
|
||||
) { body ->
|
||||
val eventContent by
|
||||
remember(note.event) {
|
||||
derivedStateOf {
|
||||
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
|
||||
val newBody =
|
||||
if (editState.value is GenericLoadable.Loaded) {
|
||||
val state =
|
||||
(editState.value as? GenericLoadable.Loaded)?.loaded?.modificationToShow
|
||||
state?.value?.event?.content() ?: body
|
||||
} else {
|
||||
body
|
||||
}
|
||||
|
||||
if (!subject.isNullOrBlank() && !newBody.split("\n")[0].contains(subject)) {
|
||||
"### $subject\n$newBody"
|
||||
} else {
|
||||
newBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = eventContent)
|
||||
}
|
||||
}
|
@ -77,6 +77,21 @@ fun RefresheableFeedView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DVMStatusView(
|
||||
viewModel: FeedViewModel,
|
||||
routeForLastRead: String?,
|
||||
enablePullRefresh: Boolean = true,
|
||||
scrollStateKey: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
viewModel.invalidateData()
|
||||
SaveableFeedState(viewModel, scrollStateKey) { listState ->
|
||||
RenderFeedState(viewModel, accountViewModel, listState, nav, routeForLastRead)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RefresheableBox(
|
||||
viewModel: InvalidatableViewModel,
|
||||
|
@ -55,6 +55,7 @@ import com.vitorpamplona.amethyst.ui.dal.HashtagFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.HomeConversationsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.NIP90ContentDiscoveryFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.NIP90StatusFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.ThreadFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.UserProfileAppRecommendationsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.UserProfileBookmarksFeedFilter
|
||||
@ -283,12 +284,22 @@ class NostrBookmarkPrivateFeedViewModel(val account: Account) :
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrNIP90ContentDiscoveryFeedViewModel(val account: Account, val dvmkey: String) :
|
||||
// FeedViewModel(BookmarkPrivateFeedFilter(account)) {
|
||||
FeedViewModel(NIP90ContentDiscoveryFilter(account, dvmkey)) {
|
||||
class Factory(val account: Account, val dvmkey: String) : ViewModelProvider.Factory {
|
||||
class NostrNIP90ContentDiscoveryFeedViewModel(val account: Account, val dvmkey: String, val requestid: String) :
|
||||
FeedViewModel(NIP90ContentDiscoveryFilter(account, dvmkey, requestid)) {
|
||||
class Factory(val account: Account, val dvmkey: String, val requestid: String) : ViewModelProvider.Factory {
|
||||
override fun <NostrNIP90ContentDiscoveryFeedViewModel : ViewModel> create(modelClass: Class<NostrNIP90ContentDiscoveryFeedViewModel>): NostrNIP90ContentDiscoveryFeedViewModel {
|
||||
return NostrNIP90ContentDiscoveryFeedViewModel(account, dvmkey) as NostrNIP90ContentDiscoveryFeedViewModel
|
||||
println("FILTERREQUEST " + requestid)
|
||||
return NostrNIP90ContentDiscoveryFeedViewModel(account, dvmkey, requestid) as NostrNIP90ContentDiscoveryFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrNIP90StatusFeedViewModel(val account: Account, val dvmkey: String, val requestid: String) :
|
||||
FeedViewModel(NIP90StatusFilter(account, dvmkey, requestid)) {
|
||||
class Factory(val account: Account, val dvmkey: String, val requestid: String) : ViewModelProvider.Factory {
|
||||
override fun <NostrNIP90StatusFeedViewModel : ViewModel> create(modelClass: Class<NostrNIP90StatusFeedViewModel>): NostrNIP90StatusFeedViewModel {
|
||||
return NostrNIP90StatusFeedViewModel(account, dvmkey, requestid) as NostrNIP90StatusFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.relays.Client
|
||||
import com.vitorpamplona.amethyst.ui.screen.DVMStatusView
|
||||
import com.vitorpamplona.amethyst.ui.screen.NostrNIP90ContentDiscoveryFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.NostrNIP90StatusFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.quartz.events.NIP90ContentDiscoveryRequestEvent
|
||||
|
||||
@ -44,10 +46,34 @@ fun NIP90ContentDiscoveryScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
var requestID = ""
|
||||
val thread =
|
||||
Thread {
|
||||
try {
|
||||
NIP90ContentDiscoveryRequestEvent.create(DVMID, accountViewModel.account.signer) {
|
||||
Client.send(it)
|
||||
requestID = it.id
|
||||
println("REQUESTID: " + requestID)
|
||||
LocalCache.justConsume(it, null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
thread.start()
|
||||
thread.join()
|
||||
|
||||
val resultFeedViewModel: NostrNIP90ContentDiscoveryFeedViewModel =
|
||||
viewModel(
|
||||
key = "NostrNIP90ContentDiscoveryFeedViewModel",
|
||||
factory = NostrNIP90ContentDiscoveryFeedViewModel.Factory(accountViewModel.account, dvmkey = DVMID),
|
||||
factory = NostrNIP90ContentDiscoveryFeedViewModel.Factory(accountViewModel.account, dvmkey = DVMID, requestid = requestID),
|
||||
)
|
||||
|
||||
val statusFeedViewModel: NostrNIP90StatusFeedViewModel =
|
||||
viewModel(
|
||||
key = "NostrNIP90StatusFeedViewModel",
|
||||
factory = NostrNIP90StatusFeedViewModel.Factory(accountViewModel.account, dvmkey = DVMID, requestid = requestID),
|
||||
)
|
||||
|
||||
val userState by accountViewModel.account.decryptBookmarks.observeAsState() // TODO
|
||||
@ -56,7 +82,7 @@ fun NIP90ContentDiscoveryScreen(
|
||||
resultFeedViewModel.invalidateData()
|
||||
}
|
||||
|
||||
RenderNostrNIP90ContentDiscoveryScreen(DVMID, accountViewModel, nav, resultFeedViewModel)
|
||||
RenderNostrNIP90ContentDiscoveryScreen(DVMID, accountViewModel, nav, resultFeedViewModel, statusFeedViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -66,11 +92,11 @@ private fun RenderNostrNIP90ContentDiscoveryScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
resultFeedViewModel: NostrNIP90ContentDiscoveryFeedViewModel,
|
||||
statusFeedViewModel: NostrNIP90StatusFeedViewModel,
|
||||
) {
|
||||
Column(Modifier.fillMaxHeight()) {
|
||||
val pagerState = rememberPagerState { 2 }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
// TODO Render a nice header with image and DVM name from the id
|
||||
|
||||
/* if (DVMID != null) {
|
||||
@ -88,26 +114,22 @@ private fun RenderNostrNIP90ContentDiscoveryScreen(
|
||||
}
|
||||
} */
|
||||
|
||||
if (DVMID != null) {
|
||||
val thread =
|
||||
Thread {
|
||||
try {
|
||||
NIP90ContentDiscoveryRequestEvent.create(DVMID, accountViewModel.account.signer) {
|
||||
Client.send(it)
|
||||
LocalCache.justConsume(it, null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
// TODO only show this when the feed below hasnt loaded. I this possible?
|
||||
// TODO render this more as a status label rather than a note
|
||||
|
||||
thread.start()
|
||||
}
|
||||
DVMStatusView(
|
||||
statusFeedViewModel,
|
||||
null,
|
||||
enablePullRefresh = false,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
HorizontalPager(state = pagerState) {
|
||||
RefresheableFeedView(
|
||||
resultFeedViewModel,
|
||||
null,
|
||||
enablePullRefresh = false,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
@ -36,7 +36,7 @@ class NIP90StatusEvent(
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
companion object {
|
||||
const val KIND = 7000
|
||||
const val ALT = "NIP90 Content Discovery reply"
|
||||
const val ALT = "NIP90 Status update"
|
||||
|
||||
fun create(
|
||||
signer: NostrSigner,
|
||||
|
Loading…
x
Reference in New Issue
Block a user