- Adds Support for Kind 16

- Migrates citations to `e` when they were supposed to be `a`
- Removes original tags from Reposts
-
This commit is contained in:
Vitor Pamplona
2023-06-17 21:08:33 -04:00
parent 611305a406
commit 05fb408842
18 changed files with 220 additions and 38 deletions
@@ -331,9 +331,15 @@ class Account(
}
note.event?.let {
val event = RepostEvent.create(it, loggedIn.privKey!!)
Client.send(event)
LocalCache.consume(event)
if (it.kind() == 1) {
val event = RepostEvent.create(it, loggedIn.privKey!!)
Client.send(event)
LocalCache.consume(event)
} else {
val event = GenericRepostEvent.create(it, loggedIn.privKey!!)
Client.send(event)
LocalCache.consume(event)
}
}
}
@@ -1111,7 +1117,7 @@ class Account(
return note.author?.let { isAcceptable(it) } ?: true && // if user hasn't hided this author
isAcceptableDirect(note) &&
(
note.event !is RepostEvent ||
(note.event !is RepostEvent && note.event !is GenericRepostEvent) ||
(note.replyTo?.firstOrNull { isAcceptableDirect(it) } != null)
) // is not a reaction about a blocked post
}
@@ -1119,7 +1125,7 @@ class Account(
fun getRelevantReports(note: Note): Set<Note> {
val followsPlusMe = userProfile().latestContactList?.verifiedFollowKeySetAndMe ?: emptySet()
val innerReports = if (note.event is RepostEvent) {
val innerReports = if (note.event is RepostEvent || note.event is GenericRepostEvent) {
note.replyTo?.map { getRelevantReports(it) }?.flatten() ?: emptyList()
} else {
emptyList()
@@ -70,7 +70,14 @@ object LocalCache {
return checkGetOrCreateAddressableNote(key)
}
if (isValidHexNpub(key)) {
return getOrCreateNote(key)
val note = getOrCreateNote(key)
val noteEvent = note.event
if (noteEvent is AddressableEvent) {
// upgrade to the latest
return checkGetOrCreateAddressableNote(noteEvent.address().toTag())
} else {
return note
}
}
return null
}
@@ -185,6 +192,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
// Already processed this event.
@@ -245,6 +253,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
if (relay != null) {
@@ -316,6 +325,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
if (note.event?.id() == event.id()) return
@@ -334,6 +344,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
if (note.event?.id() == event.id()) return
@@ -352,6 +363,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
if (note.event?.id() == event.id()) return
@@ -370,6 +382,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
// Already processed this event.
@@ -389,6 +402,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
// Already processed this event.
@@ -408,6 +422,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
// Already processed this event.
@@ -451,6 +466,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
// Already processed this event.
@@ -470,6 +486,7 @@ object LocalCache {
if (version.event == null) {
version.loadEvent(event, author, emptyList())
version.moveAllReferencesTo(note)
}
// Already processed this event.
@@ -605,6 +622,31 @@ object LocalCache {
refreshObservers(note)
}
fun consume(event: GenericRepostEvent) {
val note = getOrCreateNote(event.id)
// Already processed this event.
if (note.event != null) return
// Log.d("TN", "New Boost (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
val author = getOrCreateUser(event.pubKey)
val repliesTo = event.boostedPost().mapNotNull { checkGetOrCreateNote(it) } +
event.taggedAddresses().mapNotNull { getOrCreateAddressableNote(it) }
note.loadEvent(event, author, repliesTo)
// Prepares user's profile view.
author.addNote(note)
// Counts the replies
repliesTo.forEach {
it.addBoost(note)
}
refreshObservers(note)
}
fun consume(event: ReactionEvent) {
val note = getOrCreateNote(event.id)
@@ -1154,6 +1196,12 @@ object LocalCache {
}
consume(event)
}
is GenericRepostEvent -> {
event.containedPost()?.let {
verifyAndConsume(it, relay)
}
consume(event)
}
is TextNoteEvent -> consume(event, relay)
is PollNoteEvent -> consume(event, relay)
else -> {
@@ -10,6 +10,7 @@ import com.vitorpamplona.amethyst.service.model.*
import com.vitorpamplona.amethyst.service.nip19.Nip19
import com.vitorpamplona.amethyst.service.relays.EOSETime
import com.vitorpamplona.amethyst.service.relays.Relay
import com.vitorpamplona.amethyst.ui.actions.updated
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
import com.vitorpamplona.amethyst.ui.note.toShortenHex
import fr.acinq.secp256k1.Hex
@@ -96,7 +97,7 @@ open class Note(val idHex: String) {
*/
fun replyLevelSignature(cachedSignatures: MutableMap<Note, String> = mutableMapOf()): String {
val replyTo = replyTo
if (replyTo == null || replyTo.isEmpty()) {
if (event is RepostEvent || event is GenericRepostEvent || replyTo == null || replyTo.isEmpty()) {
return "/" + formattedDateTime(createdAt() ?: 0) + ";"
}
@@ -109,7 +110,7 @@ open class Note(val idHex: String) {
fun replyLevel(cachedLevels: MutableMap<Note, Int> = mutableMapOf()): Int {
val replyTo = replyTo
if (replyTo == null || replyTo.isEmpty()) {
if (event is RepostEvent || event is GenericRepostEvent || replyTo == null || replyTo.isEmpty()) {
return 0
}
@@ -372,7 +373,7 @@ open class Note(val idHex: String) {
}
fun isNewThread(): Boolean {
return event is RepostEvent || replyTo == null || replyTo?.size == 0
return event is RepostEvent || event is GenericRepostEvent || replyTo == null || replyTo?.size == 0
}
fun hasZapped(loggedIn: User): Boolean {
@@ -400,6 +401,42 @@ open class Note(val idHex: String) {
return boosts.filter { it.author == loggedIn }
}
fun moveAllReferencesTo(note: AddressableNote) {
// migrates these comments to a new version
replies.forEach {
note.addReply(it)
it.replyTo = it.replyTo?.updated(this, note)
}
reactions.forEach {
it.value.forEach {
note.addReaction(it)
it.replyTo = it.replyTo?.updated(this, note)
}
}
boosts.forEach {
note.addBoost(it)
it.replyTo = it.replyTo?.updated(this, note)
}
reports.forEach {
it.value.forEach {
note.addReport(it)
it.replyTo = it.replyTo?.updated(this, note)
}
}
zaps.forEach {
note.addZap(it.key, it.value)
it.key.replyTo = it.key.replyTo?.updated(this, note)
it.value?.replyTo = it.value?.replyTo?.updated(this, note)
}
replyTo = null
replies = emptySet()
reactions = emptyMap()
boosts = emptySet()
reports = emptyMap()
zaps = emptyMap()
}
var liveSet: NoteLiveSet? = null
fun live(): NoteLiveSet {
@@ -2,6 +2,8 @@ package com.vitorpamplona.amethyst.model
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.model.ATag
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent
import com.vitorpamplona.amethyst.service.model.RepostEvent
import kotlin.time.ExperimentalTime
import kotlin.time.measureTimedValue
@@ -10,6 +12,8 @@ class ThreadAssembler {
private fun searchRoot(note: Note, testedNotes: MutableSet<Note> = mutableSetOf()): Note? {
if (note.replyTo == null || note.replyTo?.isEmpty() == true) return note
if (note.event is RepostEvent || note.event is GenericRepostEvent) return note
testedNotes.add(note)
val markedAsRoot = note.event?.tags()?.firstOrNull { it[0] == "e" && it.size > 3 && it[3] == "root" }?.getOrNull(1)
@@ -88,6 +88,7 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
PollNoteEvent.kind,
ReactionEvent.kind,
RepostEvent.kind,
GenericRepostEvent.kind,
ReportEvent.kind,
LnZapEvent.kind,
LnZapPaymentResponseEvent.kind,
@@ -3,6 +3,7 @@ package com.vitorpamplona.amethyst.service
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.UserState
import com.vitorpamplona.amethyst.service.model.AudioTrackEvent
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent
import com.vitorpamplona.amethyst.service.model.HighlightEvent
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
import com.vitorpamplona.amethyst.service.model.PinListEvent
@@ -59,7 +60,16 @@ object NostrHomeDataSource : NostrDataSource("HomeFeed") {
return TypedFilter(
types = setOf(FeedType.FOLLOWS),
filter = JsonFilter(
kinds = listOf(TextNoteEvent.kind, RepostEvent.kind, LongTextNoteEvent.kind, PollNoteEvent.kind, HighlightEvent.kind, AudioTrackEvent.kind, PinListEvent.kind),
kinds = listOf(
TextNoteEvent.kind,
RepostEvent.kind,
GenericRepostEvent.kind,
LongTextNoteEvent.kind,
PollNoteEvent.kind,
HighlightEvent.kind,
AudioTrackEvent.kind,
PinListEvent.kind
),
authors = followSet,
limit = 400,
since = latestEOSEs.users[account.userProfile()]?.followList?.get(account.defaultHomeFollowList)?.relayList
@@ -26,7 +26,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
filter = JsonFilter(
kinds = listOf(
TextNoteEvent.kind, LongTextNoteEvent.kind,
ReactionEvent.kind, RepostEvent.kind, ReportEvent.kind,
ReactionEvent.kind, RepostEvent.kind, GenericRepostEvent.kind, ReportEvent.kind,
LnZapEvent.kind, LnZapRequestEvent.kind,
BadgeAwardEvent.kind, BadgeDefinitionEvent.kind, BadgeProfilesEvent.kind,
PollNoteEvent.kind, AudioTrackEvent.kind, PinListEvent.kind,
@@ -77,6 +77,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
LongTextNoteEvent.kind,
ReactionEvent.kind,
RepostEvent.kind,
GenericRepostEvent.kind,
ReportEvent.kind,
LnZapEvent.kind,
LnZapRequestEvent.kind,
@@ -28,7 +28,16 @@ object NostrUserProfileDataSource : NostrDataSource("UserProfileFeed") {
TypedFilter(
types = COMMON_FEED_TYPES,
filter = JsonFilter(
kinds = listOf(TextNoteEvent.kind, RepostEvent.kind, LongTextNoteEvent.kind, AudioTrackEvent.kind, PinListEvent.kind, PollNoteEvent.kind, HighlightEvent.kind),
kinds = listOf(
TextNoteEvent.kind,
GenericRepostEvent.kind,
RepostEvent.kind,
LongTextNoteEvent.kind,
AudioTrackEvent.kind,
PinListEvent.kind,
PollNoteEvent.kind,
HighlightEvent.kind
),
authors = listOf(it.pubkeyHex),
limit = 200
)
@@ -245,6 +245,7 @@ open class Event(
FileHeaderEvent.kind -> FileHeaderEvent(id, pubKey, createdAt, tags, content, sig)
FileStorageEvent.kind -> FileStorageEvent(id, pubKey, createdAt, tags, content, sig)
FileStorageHeaderEvent.kind -> FileStorageHeaderEvent(id, pubKey, createdAt, tags, content, sig)
GenericRepostEvent.kind -> GenericRepostEvent(id, pubKey, createdAt, tags, content, sig)
HighlightEvent.kind -> HighlightEvent(id, pubKey, createdAt, tags, content, sig)
LiveActivitiesEvent.kind -> LiveActivitiesEvent(id, pubKey, createdAt, tags, content, sig)
LnZapEvent.kind -> LnZapEvent(id, pubKey, createdAt, tags, content, sig)
@@ -0,0 +1,52 @@
package com.vitorpamplona.amethyst.service.model
import androidx.compose.runtime.Immutable
import com.vitorpamplona.amethyst.model.HexKey
import com.vitorpamplona.amethyst.model.toHexKey
import com.vitorpamplona.amethyst.service.relays.Client
import nostr.postr.Utils
import java.util.Date
@Immutable
class GenericRepostEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
tags: List<List<String>>,
content: String,
sig: HexKey
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
fun boostedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }
fun originalAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) }
fun containedPost() = try {
fromJson(content, Client.lenient)
} catch (e: Exception) {
null
}
companion object {
const val kind = 16
fun create(boostedPost: EventInterface, privateKey: ByteArray, createdAt: Long = Date().time / 1000): GenericRepostEvent {
val content = boostedPost.toJson()
val replyToPost = listOf("e", boostedPost.id())
val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) {
tags = tags + listOf(listOf("a", boostedPost.address().toTag()))
}
tags = tags + listOf(listOf("k", "${boostedPost.kind()}"))
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = Utils.sign(id, privateKey)
return GenericRepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
}
}
}
@@ -36,7 +36,7 @@ class RepostEvent(
val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
var tags: List<List<String>> = boostedPost.tags().plus(listOf(replyToPost, replyToAuthor))
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) {
tags = tags + listOf(listOf("a", boostedPost.address().toTag()))
@@ -4,6 +4,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.model.AudioTrackEvent
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent
import com.vitorpamplona.amethyst.service.model.HighlightEvent
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
import com.vitorpamplona.amethyst.service.model.PollNoteEvent
@@ -26,19 +27,24 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
val followingKeySet = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
val followingTagSet = account.selectedTagsFollowList(account.defaultHomeFollowList) ?: emptySet()
val oneHr = 60 * 60
return collection
.asSequence()
.filter { it ->
val noteEvent = it.event
(noteEvent is TextNoteEvent || noteEvent is RepostEvent || noteEvent is LongTextNoteEvent || noteEvent is PollNoteEvent || noteEvent is HighlightEvent || noteEvent is AudioTrackEvent) &&
(noteEvent is TextNoteEvent || noteEvent is RepostEvent || noteEvent is GenericRepostEvent || noteEvent is LongTextNoteEvent || noteEvent is PollNoteEvent || noteEvent is HighlightEvent || noteEvent is AudioTrackEvent) &&
(it.author?.pubkeyHex in followingKeySet || (noteEvent.isTaggedHashes(followingTagSet))) &&
// && account.isAcceptable(it) // This filter follows only. No need to check if acceptable
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true &&
it.isNewThread() &&
(
noteEvent !is RepostEvent || // not a repost
(it.replyTo?.lastOrNull()?.author?.pubkeyHex !in followingKeySet) // or a repost of by a non-follower's post (likely not seen yet)
) // || (noteEvent.createdAt > (it.replyTo?.lastOrNull()?.createdAt() ?: 0) + 60*60*1000 )) //
(noteEvent !is RepostEvent && noteEvent !is GenericRepostEvent) || // not a repost
(
it.replyTo?.lastOrNull()?.author?.pubkeyHex !in followingKeySet ||
(noteEvent.createdAt() > (it.replyTo?.lastOrNull()?.createdAt() ?: 0) + oneHr)
) // or a repost of by a non-follower's post (likely not seen yet)
)
}
.toSet()
}
@@ -61,7 +61,7 @@ class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
return note.replyTo?.lastOrNull()?.author?.pubkeyHex == authorHex
}
if (event is RepostEvent) {
if (event is RepostEvent || event is GenericRepostEvent) {
return note.replyTo?.lastOrNull()?.author?.pubkeyHex == authorHex
}
@@ -98,6 +98,7 @@ import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
import com.vitorpamplona.amethyst.service.model.EventInterface
import com.vitorpamplona.amethyst.service.model.FileHeaderEvent
import com.vitorpamplona.amethyst.service.model.FileStorageHeaderEvent
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent
import com.vitorpamplona.amethyst.service.model.HighlightEvent
import com.vitorpamplona.amethyst.service.model.LiveActivitiesEvent
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
@@ -520,7 +521,7 @@ private fun RenderNoteWithReactions(
val showSecondRow by remember {
derivedStateOf {
baseNote.event !is RepostEvent && !isBoostedNote && !isQuotedNote
baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent && !isBoostedNote && !isQuotedNote
}
}
@@ -554,7 +555,7 @@ private fun RenderNoteWithReactions(
)
}
if (!makeItShort && baseNote.event !is RepostEvent) {
if (!makeItShort && baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent) {
ReactionsRow(
baseNote = baseNote,
showReactionDetail = notBoostedNorQuote,
@@ -562,7 +563,7 @@ private fun RenderNoteWithReactions(
nav = nav
)
} else {
if (!isBoostedNote && baseNote.event !is RepostEvent) {
if (!isBoostedNote && baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent) {
Spacer(modifier = Modifier.height(10.dp))
}
}
@@ -648,6 +649,10 @@ private fun RenderNoteRow(
RenderRepost(baseNote, backgroundColor, accountViewModel, nav)
}
is GenericRepostEvent -> {
RenderRepost(baseNote, backgroundColor, accountViewModel, nav)
}
is ReportEvent -> {
RenderReport(baseNote, backgroundColor, accountViewModel, nav)
}
@@ -1421,7 +1426,7 @@ private fun RenderReaction(
}
@Composable
private fun RenderRepost(
fun RenderRepost(
note: Note,
backgroundColor: MutableState<Color>,
accountViewModel: AccountViewModel,
@@ -1754,7 +1759,7 @@ private fun FirstUserInfoRow(
NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) })
}
if (eventNote is RepostEvent) {
if (eventNote is RepostEvent || eventNote is GenericRepostEvent) {
Text(
" ${stringResource(id = R.string.boosted)}",
fontWeight = FontWeight.Bold,
@@ -1828,7 +1833,7 @@ private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel,
Box(modifier = modifier, contentAlignment = Alignment.BottomEnd) {
NoteAuthorPicture(baseNote, nav, accountViewModel, 55.dp)
if (baseNote.event is RepostEvent) {
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
RepostNoteAuthorPicture(baseNote, accountViewModel, nav)
}
@@ -1839,7 +1844,7 @@ private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel,
}
}
if (baseNote.event is RepostEvent) {
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
val baseReply = remember {
baseNote.replyTo?.lastOrNull()
}
@@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent
import com.vitorpamplona.amethyst.service.model.LnZapEvent
import com.vitorpamplona.amethyst.service.model.ReactionEvent
import com.vitorpamplona.amethyst.service.model.RepostEvent
@@ -178,9 +179,9 @@ class UserReactionsViewModel(val account: Account) : ViewModel() {
reactions[netDate] = (reactions[netDate] ?: 0) + 1
takenIntoAccount.add(noteEvent.id())
}
} else if (noteEvent is RepostEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
val netDate = formatDate(noteEvent.createdAt)
} else if (noteEvent is RepostEvent || noteEvent is GenericRepostEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey() != currentUser) {
val netDate = formatDate(noteEvent.createdAt())
boosts[netDate] = (boosts[netDate] ?: 0) + 1
takenIntoAccount.add(noteEvent.id())
}
@@ -229,9 +230,9 @@ class UserReactionsViewModel(val account: Account) : ViewModel() {
takenIntoAccount.add(noteEvent.id())
hasNewElements = true
}
} else if (noteEvent is RepostEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
val netDate = formatDate(noteEvent.createdAt)
} else if (noteEvent is RepostEvent || noteEvent is GenericRepostEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey() != currentUser) {
val netDate = formatDate(noteEvent.createdAt())
boosts[netDate] = (boosts[netDate] ?: 0) + 1
takenIntoAccount.add(noteEvent.id())
hasNewElements = true
@@ -15,6 +15,7 @@ import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent
import com.vitorpamplona.amethyst.service.model.LnZapEvent
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
import com.vitorpamplona.amethyst.service.model.ReactionEvent
@@ -162,7 +163,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
val boostsPerEvent = mutableMapOf<Note, MutableList<Note>>()
notes
.filter { it.event is RepostEvent }
.filter { it.event is RepostEvent || it.event is GenericRepostEvent }
.forEach {
val boostedPost = it.replyTo?.lastOrNull() { it.event !is ChannelMetadataEvent && it.event !is ChannelCreateEvent }
if (boostedPost != null) {
@@ -197,7 +198,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
)
}
val textNoteCards = notes.filter { it.event !is ReactionEvent && it.event !is RepostEvent && it.event !is LnZapEvent }.map {
val textNoteCards = notes.filter { it.event !is ReactionEvent && it.event !is RepostEvent && it.event !is GenericRepostEvent && it.event !is LnZapEvent }.map {
if (it.event is PrivateDmEvent) {
MessageSetCard(it)
} else if (it.event is BadgeAwardEvent) {
@@ -57,12 +57,14 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.model.AppDefinitionEvent
import com.vitorpamplona.amethyst.service.model.AudioTrackEvent
import com.vitorpamplona.amethyst.service.model.BadgeDefinitionEvent
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent
import com.vitorpamplona.amethyst.service.model.HighlightEvent
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
import com.vitorpamplona.amethyst.service.model.PeopleListEvent
import com.vitorpamplona.amethyst.service.model.PinListEvent
import com.vitorpamplona.amethyst.service.model.PollNoteEvent
import com.vitorpamplona.amethyst.service.model.RelaySetEvent
import com.vitorpamplona.amethyst.service.model.RepostEvent
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.note.*
import com.vitorpamplona.amethyst.ui.note.BadgeDisplay
@@ -401,6 +403,8 @@ fun NoteMaster(
accountViewModel,
nav
)
} else if (noteEvent is RepostEvent || noteEvent is GenericRepostEvent) {
RenderRepost(baseNote, backgroundColor, accountViewModel, nav)
} else if (noteEvent is PollNoteEvent) {
val canPreview = note.author == account.userProfile() ||
(note.author?.let { account.userProfile().isFollowingCached(it) } ?: true) ||
@@ -2,14 +2,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -55,9 +53,7 @@ fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, nav: (Stri
}
Column(Modifier.fillMaxHeight()) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
Column() {
ThreadFeedView(noteId, feedViewModel, accountViewModel, nav)
}
}