Adds view support for public messages
This commit is contained in:
@@ -49,6 +49,7 @@ import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.RelationshipStatusEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.checkSignature
|
||||
@@ -84,7 +85,7 @@ import com.vitorpamplona.quartz.nip03Timestamp.VerificationState
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionIndex
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
@@ -535,6 +536,12 @@ object LocalCache : ILocalCache {
|
||||
wasVerified: Boolean,
|
||||
) = consumeRegularEvent(event, relay, wasVerified)
|
||||
|
||||
fun consume(
|
||||
event: PublicMessageEvent,
|
||||
relay: NormalizedRelayUrl? = null,
|
||||
wasVerified: Boolean,
|
||||
) = consumeRegularEvent(event, relay, wasVerified)
|
||||
|
||||
fun consume(
|
||||
event: TorrentEvent,
|
||||
relay: NormalizedRelayUrl?,
|
||||
@@ -575,7 +582,7 @@ object LocalCache : ILocalCache {
|
||||
// Already processed this event.
|
||||
if (note.event != null) return false
|
||||
|
||||
if (event is BaseThreadedEvent && antiSpam.isSpam(event, relay)) {
|
||||
if (event is BaseNoteEvent && antiSpam.isSpam(event, relay)) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2813,6 +2820,7 @@ object LocalCache : ILocalCache {
|
||||
is PrivateOutboxRelayListEvent -> consume(event, relay, wasVerified)
|
||||
is ProxyRelayListEvent -> consume(event, relay, wasVerified)
|
||||
is PinListEvent -> consume(event, relay, wasVerified)
|
||||
is PublicMessageEvent -> consume(event, relay, wasVerified)
|
||||
is PeopleListEvent -> consume(event, relay, wasVerified)
|
||||
is PollNoteEvent -> consume(event, relay, wasVerified)
|
||||
is ReactionEvent -> consume(event, relay, wasVerified)
|
||||
|
||||
+2
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.nip01N
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStorySceneEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
@@ -63,6 +64,7 @@ val NotificationsPerKeyKinds =
|
||||
EphemeralChatEvent.KIND,
|
||||
BadgeAwardEvent.KIND,
|
||||
PollNoteEvent.KIND,
|
||||
PublicMessageEvent.KIND,
|
||||
)
|
||||
|
||||
val NotificationsPerKeyKinds2 =
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStorySceneEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
@@ -69,7 +70,7 @@ val SearchPostsByTextKinds2 =
|
||||
EmojiPackEvent.KIND,
|
||||
HighlightEvent.KIND,
|
||||
LiveActivitiesEvent.KIND,
|
||||
PollNoteEvent.KIND,
|
||||
PublicMessageEvent.KIND,
|
||||
NNSEvent.KIND,
|
||||
WikiNoteEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
|
||||
@@ -119,12 +119,14 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderPinListEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPoll
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPostApproval
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPrivateMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPublicMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderReport
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderTextEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderTextModificationEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderTorrent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderTorrentComment
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderVoiceTrack
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderWikiContent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.VideoDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -160,6 +162,7 @@ import com.vitorpamplona.quartz.experimental.forks.isAFork
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
|
||||
import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.isTaggedAddressableKind
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope
|
||||
@@ -214,6 +217,7 @@ import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
@@ -790,7 +794,7 @@ private fun RenderNoteRow(
|
||||
is VideoHorizontalEvent -> VideoDisplay(baseNote, makeItShort, canPreview, backgroundColor, ContentScale.FillWidth, accountViewModel, nav)
|
||||
is VideoVerticalEvent -> VideoDisplay(baseNote, makeItShort, canPreview, backgroundColor, ContentScale.FillWidth, accountViewModel, nav)
|
||||
is PictureEvent -> PictureDisplay(baseNote, true, ContentScale.FillWidth, PaddingValues(vertical = 5.dp), backgroundColor, accountViewModel, nav)
|
||||
|
||||
is BaseVoiceEvent -> RenderVoiceTrack(baseNote, ContentScale.FillWidth, accountViewModel, nav)
|
||||
is FileStorageHeaderEvent -> FileStorageHeaderDisplay(baseNote, true, ContentScale.FillWidth, accountViewModel)
|
||||
is CommunityPostApprovalEvent -> {
|
||||
RenderPostApproval(
|
||||
@@ -865,7 +869,16 @@ private fun RenderNoteRow(
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
|
||||
is PublicMessageEvent ->
|
||||
RenderPublicMessage(
|
||||
baseNote,
|
||||
makeItShort,
|
||||
canPreview,
|
||||
quotesLeft,
|
||||
backgroundColor,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
else -> {
|
||||
RenderTextEvent(
|
||||
baseNote,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2025 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.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
|
||||
import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
|
||||
|
||||
@Composable
|
||||
fun RenderPublicMessage(
|
||||
note: Note,
|
||||
makeItShort: Boolean,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? PublicMessageEvent ?: return
|
||||
|
||||
if (makeItShort && accountViewModel.isLoggedUser(note.author)) {
|
||||
Text(
|
||||
text = noteEvent.content,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
} else {
|
||||
val callbackUri = remember(note) { note.toNostrUri() }
|
||||
|
||||
SensitivityWarning(
|
||||
note = note,
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
TranslatableRichTextViewer(
|
||||
content = noteEvent.content,
|
||||
canPreview = canPreview && !makeItShort,
|
||||
quotesLeft = quotesLeft,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
tags = remember(noteEvent) { noteEvent.tags.toImmutableListOfLists() },
|
||||
backgroundColor = backgroundColor,
|
||||
id = note.idHex,
|
||||
callbackUri = callbackUri,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
if (noteEvent.hasHashtags()) {
|
||||
DisplayUncitedHashtags(noteEvent, noteEvent.content, callbackUri, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -52,7 +52,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.util.Consumer
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
@@ -173,7 +172,7 @@ private fun NewPostScreenInner(
|
||||
onPost = {
|
||||
// uses the accountViewModel scope to avoid cancelling this
|
||||
// function when the postViewModel is released
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.runIOCatching {
|
||||
postViewModel.sendPostSync()
|
||||
delay(100)
|
||||
nav.popBack()
|
||||
@@ -182,7 +181,7 @@ private fun NewPostScreenInner(
|
||||
onCancel = {
|
||||
// uses the accountViewModel scope to avoid cancelling this
|
||||
// function when the postViewModel is released
|
||||
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
|
||||
accountViewModel.runIOCatching {
|
||||
postViewModel.sendDraftSync()
|
||||
nav.popBack()
|
||||
postViewModel.cancel()
|
||||
|
||||
+21
-31
@@ -136,6 +136,9 @@ open class ShortNotePostViewModel :
|
||||
IZapRaiser {
|
||||
val draftTag = DraftTagState()
|
||||
|
||||
lateinit var accountViewModel: AccountViewModel
|
||||
lateinit var account: Account
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
draftTag.versions.collectLatest {
|
||||
@@ -147,9 +150,6 @@ open class ShortNotePostViewModel :
|
||||
}
|
||||
}
|
||||
|
||||
var accountViewModel: AccountViewModel? = null
|
||||
var account: Account? = null
|
||||
|
||||
var originalNote: Note? by mutableStateOf(null)
|
||||
var forkedFromNote: Note? by mutableStateOf(null)
|
||||
|
||||
@@ -157,8 +157,7 @@ open class ShortNotePostViewModel :
|
||||
var eTags by mutableStateOf<List<Note>?>(null)
|
||||
|
||||
val iMetaAttachments = IMetaAttachments()
|
||||
var nip95attachments by
|
||||
mutableStateOf<List<Pair<FileStorageEvent, FileStorageHeaderEvent>>>(emptyList())
|
||||
var nip95attachments by mutableStateOf<List<Pair<FileStorageEvent, FileStorageHeaderEvent>>>(emptyList())
|
||||
|
||||
override var message by mutableStateOf(TextFieldValue(""))
|
||||
|
||||
@@ -213,11 +212,11 @@ open class ShortNotePostViewModel :
|
||||
var wantsZapRaiser by mutableStateOf(false)
|
||||
override val zapRaiserAmount = mutableStateOf<Long?>(null)
|
||||
|
||||
fun lnAddress(): String? = account?.userProfile()?.info?.lnAddress()
|
||||
fun lnAddress(): String? = account.userProfile().info?.lnAddress()
|
||||
|
||||
fun hasLnAddress(): Boolean = account?.userProfile()?.info?.lnAddress() != null
|
||||
fun hasLnAddress(): Boolean = account.userProfile().info?.lnAddress() != null
|
||||
|
||||
fun user(): User? = account?.userProfile()
|
||||
fun user(): User? = account.userProfile()
|
||||
|
||||
open fun init(accountVM: AccountViewModel) {
|
||||
this.accountViewModel = accountVM
|
||||
@@ -239,7 +238,6 @@ open class ShortNotePostViewModel :
|
||||
version: Note?,
|
||||
draft: Note?,
|
||||
) {
|
||||
val accountViewModel = accountViewModel ?: return
|
||||
val noteEvent = draft?.event
|
||||
val noteAuthor = draft?.author
|
||||
|
||||
@@ -369,8 +367,8 @@ open class ShortNotePostViewModel :
|
||||
}
|
||||
|
||||
private fun loadFromDraft(draftEvent: TextNoteEvent) {
|
||||
canAddInvoice = accountViewModel?.userProfile()?.info?.lnAddress() != null
|
||||
canAddZapRaiser = accountViewModel?.userProfile()?.info?.lnAddress() != null
|
||||
canAddInvoice = accountViewModel.userProfile().info?.lnAddress() != null
|
||||
canAddZapRaiser = accountViewModel.userProfile().info?.lnAddress() != null
|
||||
multiOrchestrator = null
|
||||
|
||||
val localForwardZapTo = draftEvent.tags.filter { it.size > 1 && it[0] == "zap" }
|
||||
@@ -475,15 +473,15 @@ open class ShortNotePostViewModel :
|
||||
}
|
||||
}
|
||||
|
||||
accountViewModel?.account?.signAndComputeBroadcast(template, extraNotesToBroadcast)
|
||||
accountViewModel.account.signAndComputeBroadcast(template, extraNotesToBroadcast)
|
||||
|
||||
accountViewModel?.deleteDraft(draftTag.current)
|
||||
accountViewModel.deleteDraft(draftTag.current)
|
||||
|
||||
cancel()
|
||||
}
|
||||
|
||||
suspend fun sendDraftSync() {
|
||||
val accountViewModel = accountViewModel ?: return
|
||||
val accountViewModel = accountViewModel
|
||||
|
||||
if (message.text.isBlank()) {
|
||||
accountViewModel.account.deleteDraft(draftTag.current)
|
||||
@@ -492,24 +490,19 @@ open class ShortNotePostViewModel :
|
||||
accountViewModel.account.createAndSendDraft(draftTag.current, template)
|
||||
|
||||
nip95attachments.forEach {
|
||||
account?.sendToPrivateOutboxAndLocal(it.first)
|
||||
account?.sendToPrivateOutboxAndLocal(it.second)
|
||||
account.sendToPrivateOutboxAndLocal(it.first)
|
||||
account.sendToPrivateOutboxAndLocal(it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createTemplate(): EventTemplate<out Event>? {
|
||||
if (accountViewModel == null) {
|
||||
cancel()
|
||||
return null
|
||||
}
|
||||
|
||||
val tagger =
|
||||
NewMessageTagger(
|
||||
message.text,
|
||||
pTags,
|
||||
eTags,
|
||||
accountViewModel!!,
|
||||
accountViewModel,
|
||||
)
|
||||
tagger.run()
|
||||
|
||||
@@ -518,7 +511,7 @@ open class ShortNotePostViewModel :
|
||||
val geoHash = (location?.value as? LocationState.LocationResult.Success)?.geoHash?.toString()
|
||||
val localZapRaiserAmount = if (wantsZapRaiser) zapRaiserAmount.value else null
|
||||
|
||||
val emojis = findEmoji(tagger.message, account?.emoji?.myEmojis?.value)
|
||||
val emojis = findEmoji(tagger.message, account.emoji.myEmojis.value)
|
||||
val urls = findURLs(tagger.message)
|
||||
val usedAttachments = iMetaAttachments.filterIsIn(urls.toSet())
|
||||
|
||||
@@ -607,8 +600,6 @@ open class ShortNotePostViewModel :
|
||||
context: Context,
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val myAccount = account ?: return@launch
|
||||
|
||||
val myMultiOrchestrator = multiOrchestrator ?: return@launch
|
||||
|
||||
isUploadingImage = true
|
||||
@@ -619,16 +610,16 @@ open class ShortNotePostViewModel :
|
||||
contentWarningReason,
|
||||
MediaCompressor.intToCompressorQuality(mediaQuality),
|
||||
server,
|
||||
myAccount,
|
||||
account,
|
||||
context,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
results.successful.forEach { state ->
|
||||
if (state.result is UploadOrchestrator.OrchestratorResult.NIP95Result) {
|
||||
val nip95 = myAccount.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason)
|
||||
val nip95 = account.createNip95(state.result.bytes, headerInfo = state.result.fileHeader, alt, contentWarningReason)
|
||||
nip95attachments = nip95attachments + nip95
|
||||
val note = nip95.let { it1 -> account?.consumeNip95(it1.first, it1.second) }
|
||||
val note = nip95.let { it1 -> account.consumeNip95(it1.first, it1.second) }
|
||||
|
||||
note?.let {
|
||||
message = message.insertUrlAtCursor("nostr:" + it.toNEvent())
|
||||
@@ -786,8 +777,7 @@ open class ShortNotePostViewModel :
|
||||
item.link.url,
|
||||
) {
|
||||
Amethyst.instance.okHttpClients.getHttpClient(
|
||||
accountViewModel?.account?.privacyState?.shouldUseTorForImageDownload(item.link.url)
|
||||
?: false,
|
||||
accountViewModel.account.privacyState.shouldUseTorForImageDownload(item.link.url),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -870,7 +860,7 @@ open class ShortNotePostViewModel :
|
||||
override fun updateZapFromText() {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val tagger =
|
||||
NewMessageTagger(message.text, emptyList(), emptyList(), accountViewModel!!)
|
||||
NewMessageTagger(message.text, emptyList(), emptyList(), accountViewModel)
|
||||
tagger.run()
|
||||
tagger.pTags?.forEach { taggedUser ->
|
||||
if (!forwardZapTo.value.items.any { it.key == taggedUser }) {
|
||||
|
||||
+2
@@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
@@ -98,6 +99,7 @@ class HomeNewThreadFeedFilter(
|
||||
(noteEvent is WikiNoteEvent && noteEvent.content.isNotEmpty()) ||
|
||||
noteEvent is PollNoteEvent ||
|
||||
noteEvent is HighlightEvent ||
|
||||
(noteEvent is PublicMessageEvent && noteEvent.content.isNotEmpty() && noteEvent.isIncluded(account.signer.pubKey)) ||
|
||||
noteEvent is InteractiveStoryPrologueEvent ||
|
||||
noteEvent is CommentEvent ||
|
||||
noteEvent is AudioTrackEvent ||
|
||||
|
||||
+4
@@ -148,6 +148,7 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderPinListEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPoll
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPostApproval
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPrivateMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderPublicMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderTextEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderTextModificationEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderTorrent
|
||||
@@ -184,6 +185,7 @@ import com.vitorpamplona.quartz.experimental.forks.forkFromAddress
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
|
||||
import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.isTaggedAddressableKind
|
||||
@@ -642,6 +644,8 @@ private fun FullBleedNoteCompose(
|
||||
RenderDraft(baseNote, 3, true, backgroundColor, accountViewModel, nav)
|
||||
} else if (noteEvent is HighlightEvent) {
|
||||
RenderHighlight(baseNote, false, canPreview, quotesLeft = 3, backgroundColor, accountViewModel, nav)
|
||||
} else if (noteEvent is PublicMessageEvent) {
|
||||
RenderPublicMessage(baseNote, false, canPreview, quotesLeft = 3, backgroundColor, accountViewModel, nav)
|
||||
} else if (noteEvent is CommentEvent) {
|
||||
RenderTextEvent(
|
||||
baseNote,
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
|
||||
<string name="temporary_account">Log off on device lock</string>
|
||||
<string name="private_message">Private Message</string>
|
||||
<string name="public_message">Public Message</string>
|
||||
|
||||
<string name="group_relay">Chat Relay</string>
|
||||
<string name="group_relay_explanation">The relay that all users of this chat connect to</string>
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.RelationshipStatusEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
@@ -267,6 +268,7 @@ class EventFactory {
|
||||
PrivateDmEvent.KIND -> PrivateDmEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
PrivateOutboxRelayListEvent.KIND -> PrivateOutboxRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
ProxyRelayListEvent.KIND -> ProxyRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
PublicMessageEvent.KIND -> PublicMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
ReactionEvent.KIND -> ReactionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
RelationshipStatusEvent.KIND -> RelationshipStatusEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
RelayAuthEvent.KIND -> RelayAuthEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright (c) 2025 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.quartz.experimental.publicMessages
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.tags.ReceiverTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.core.any
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.pubKeyHints
|
||||
import com.vitorpamplona.quartz.nip19Bech32.pubKeys
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip50Search.SearchableEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class PublicMessageEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseNoteEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||
PubKeyHintProvider,
|
||||
SearchableEvent {
|
||||
override fun indexableContent() = content
|
||||
|
||||
override fun pubKeyHints() = tags.mapNotNull(ReceiverTag::parseAsHint) + citedNIP19().pubKeyHints()
|
||||
|
||||
override fun linkedPubKeys() = tags.mapNotNull(ReceiverTag::parseKey) + citedNIP19().pubKeys()
|
||||
|
||||
fun isIncluded(pubKey: HexKey) = tags.any(ReceiverTag::match, pubKey) || this.pubKey == pubKey
|
||||
|
||||
fun group() = tags.mapNotNullTo(mutableListOf(ReceiverTag(this.pubKey, null)), ReceiverTag::parse)
|
||||
|
||||
fun groupKeys() = tags.mapNotNullTo(mutableListOf(this.pubKey), ReceiverTag::parseKey)
|
||||
|
||||
fun groupKeySet() = tags.mapNotNullTo(mutableSetOf(this.pubKey), ReceiverTag::parseKey)
|
||||
|
||||
companion object {
|
||||
const val KIND = 24
|
||||
const val ALT_DESCRIPTION = "Public Message"
|
||||
|
||||
fun build(
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<PublicMessageEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
alt(ALT_DESCRIPTION)
|
||||
initializer()
|
||||
}
|
||||
|
||||
fun build(
|
||||
to: ReceiverTag,
|
||||
msg: String,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<PublicMessageEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, msg, createdAt) {
|
||||
alt(ALT_DESCRIPTION)
|
||||
toUser(to)
|
||||
initializer()
|
||||
}
|
||||
|
||||
fun build(
|
||||
to: List<ReceiverTag>,
|
||||
msg: String,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<PublicMessageEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, msg, createdAt) {
|
||||
alt(ALT_DESCRIPTION)
|
||||
toGroup(to)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2025 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.quartz.experimental.publicMessages
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.tags.ReceiverTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
|
||||
fun TagArrayBuilder<PublicMessageEvent>.toGroup(list: List<ReceiverTag>) = addAll(list.map { it.toTagArray() })
|
||||
|
||||
fun TagArrayBuilder<PublicMessageEvent>.toUser(user: ReceiverTag) = add(user.toTagArray())
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2025 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.quartz.experimental.publicMessages.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ReceiverTag(
|
||||
val pubKey: HexKey,
|
||||
val relayHint: NormalizedRelayUrl? = null,
|
||||
) {
|
||||
fun toNProfile(): String = NProfile.create(pubKey, relayHint?.let { listOf(it) } ?: emptyList())
|
||||
|
||||
fun toNPub(): String = pubKey.hexToByteArray().toNpub()
|
||||
|
||||
fun toTagArray() = assemble(pubKey, relayHint)
|
||||
|
||||
fun toTagIdOnly() = assemble(pubKey, null)
|
||||
|
||||
companion object Companion {
|
||||
const val TAG_NAME = "p"
|
||||
|
||||
fun isTagged(tag: Array<String>): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64
|
||||
|
||||
fun match(
|
||||
tag: Array<String>,
|
||||
key: HexKey,
|
||||
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] == key
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): ReceiverTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
|
||||
val hint = tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) }
|
||||
|
||||
return ReceiverTag(tag[1], hint)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Tag): HexKey? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Tag): PubKeyHint? {
|
||||
ensure(tag.has(2)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
|
||||
val hint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||
|
||||
ensure(hint != null) { return null }
|
||||
|
||||
return PubKeyHint(tag[1], hint)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
pubkey: HexKey,
|
||||
relayHint: NormalizedRelayUrl?,
|
||||
) = arrayOfNotNull(TAG_NAME, pubkey, relayHint?.url)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2025 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.quartz.nip10Notes
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithEventsOrAddresses
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithPeople
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.Entity
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NNote
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
||||
|
||||
@Immutable
|
||||
open class BaseNoteEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
kind: Int,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
@Transient
|
||||
private var citedUsersCache: Set<String>? = null
|
||||
|
||||
@Transient
|
||||
private var citedNotesCache: Set<String>? = null
|
||||
|
||||
@Transient
|
||||
private var citedNIP19Cache: List<Entity>? = null
|
||||
|
||||
fun citedNIP19(): List<Entity> {
|
||||
citedNIP19Cache?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
return findNostrUris(content).also { citedNIP19Cache = it }
|
||||
}
|
||||
|
||||
fun citedUsers(): Set<HexKey> {
|
||||
citedUsersCache?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val citedUsers = mutableSetOf<String>()
|
||||
|
||||
findIndexTagsWithPeople(content, tags, citedUsers)
|
||||
citedNIP19().forEach { parsed ->
|
||||
when (parsed) {
|
||||
is NProfile -> citedUsers.add(parsed.hex)
|
||||
is NPub -> citedUsers.add(parsed.hex)
|
||||
}
|
||||
}
|
||||
|
||||
citedUsersCache = citedUsers
|
||||
return citedUsers
|
||||
}
|
||||
|
||||
fun findCitations(): Set<HexKey> {
|
||||
citedNotesCache?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val citations = mutableSetOf<String>()
|
||||
|
||||
findIndexTagsWithEventsOrAddresses(content, tags, citations).toMutableSet()
|
||||
citedNIP19().forEach { entity ->
|
||||
when (entity) {
|
||||
is NEvent -> citations.add(entity.hex)
|
||||
is NAddress -> citations.add(entity.aTag())
|
||||
is NNote -> citations.add(entity.hex)
|
||||
is NEmbed -> citations.add(entity.event.id)
|
||||
}
|
||||
}
|
||||
|
||||
citedNotesCache = citations
|
||||
return citations
|
||||
}
|
||||
}
|
||||
@@ -21,21 +21,10 @@
|
||||
package com.vitorpamplona.quartz.nip10Notes
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithEventsOrAddresses
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithPeople
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.Entity
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NNote
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.utils.lastNotNullOfOrNull
|
||||
@@ -49,16 +38,7 @@ open class BaseThreadedEvent(
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
@Transient
|
||||
private var citedUsersCache: Set<String>? = null
|
||||
|
||||
@Transient
|
||||
private var citedNotesCache: Set<String>? = null
|
||||
|
||||
@Transient
|
||||
private var citedNIP19Cache: List<Entity>? = null
|
||||
|
||||
) : BaseNoteEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun mentions() = taggedUsers()
|
||||
|
||||
fun markedRoot() = tags.firstNotNullOfOrNull(MarkedETag::parseRoot)
|
||||
@@ -92,54 +72,6 @@ open class BaseThreadedEvent(
|
||||
return newStyleReply ?: newStyleRoot ?: oldStylePositional
|
||||
}
|
||||
|
||||
fun citedNIP19(): List<Entity> {
|
||||
citedNIP19Cache?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
return findNostrUris(content).also { citedNIP19Cache = it }
|
||||
}
|
||||
|
||||
fun citedUsers(): Set<HexKey> {
|
||||
citedUsersCache?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val citedUsers = mutableSetOf<String>()
|
||||
|
||||
findIndexTagsWithPeople(content, tags, citedUsers)
|
||||
citedNIP19().forEach { parsed ->
|
||||
when (parsed) {
|
||||
is NProfile -> citedUsers.add(parsed.hex)
|
||||
is NPub -> citedUsers.add(parsed.hex)
|
||||
}
|
||||
}
|
||||
|
||||
citedUsersCache = citedUsers
|
||||
return citedUsers
|
||||
}
|
||||
|
||||
fun findCitations(): Set<HexKey> {
|
||||
citedNotesCache?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val citations = mutableSetOf<String>()
|
||||
|
||||
findIndexTagsWithEventsOrAddresses(content, tags, citations).toMutableSet()
|
||||
citedNIP19().forEach { entity ->
|
||||
when (entity) {
|
||||
is NEvent -> citations.add(entity.hex)
|
||||
is NAddress -> citations.add(entity.aTag())
|
||||
is NNote -> citations.add(entity.hex)
|
||||
is NEmbed -> citations.add(entity.event.id)
|
||||
}
|
||||
}
|
||||
|
||||
citedNotesCache = citations
|
||||
return citations
|
||||
}
|
||||
|
||||
fun tagsWithoutCitations(): List<String> {
|
||||
val certainRepliesTo = markedReplyTos()
|
||||
val uncertainRepliesTo = unmarkedReplyTos()
|
||||
|
||||
Reference in New Issue
Block a user