mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-03-17 21:31:57 +01:00
Adds support for Custom Reaction selection on new posts and direct GIF url usage.
This commit is contained in:
parent
c9a6b63f67
commit
aa44560714
@ -31,6 +31,7 @@ import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.fonfon.kgeohash.GeoHash
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.BuildConfig
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.service.LocationState
|
||||
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
|
||||
@ -1063,6 +1064,80 @@ class Account(
|
||||
}
|
||||
}
|
||||
|
||||
class EmojiMedia(
|
||||
val code: String,
|
||||
val url: MediaUrlImage,
|
||||
)
|
||||
|
||||
fun getEmojiPackSelection(): EmojiPackSelectionEvent? = getEmojiPackSelectionNote().event as? EmojiPackSelectionEvent
|
||||
|
||||
fun getEmojiPackSelectionFlow(): StateFlow<NoteState> = getEmojiPackSelectionNote().flow().metadata.stateFlow
|
||||
|
||||
fun getEmojiPackSelectionNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(EmojiPackSelectionEvent.createAddressATag(userProfile().pubkeyHex))
|
||||
|
||||
fun convertEmojiSelectionPack(selection: EmojiPackSelectionEvent?): List<StateFlow<NoteState>>? =
|
||||
selection?.taggedAddresses()?.map {
|
||||
LocalCache
|
||||
.getOrCreateAddressableNote(it)
|
||||
.flow()
|
||||
.metadata.stateFlow
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val liveEmojiSelectionPack: StateFlow<List<StateFlow<NoteState>>?> by lazy {
|
||||
getEmojiPackSelectionFlow()
|
||||
.transformLatest {
|
||||
emit(convertEmojiSelectionPack(it.note.event as? EmojiPackSelectionEvent))
|
||||
}.flowOn(Dispatchers.Default)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Eagerly,
|
||||
runBlocking(Dispatchers.Default) {
|
||||
convertEmojiSelectionPack(getEmojiPackSelection())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun convertEmojiPack(pack: EmojiPackEvent): List<EmojiMedia> =
|
||||
pack.taggedEmojis().map {
|
||||
EmojiMedia(it.code, MediaUrlImage(it.url))
|
||||
}
|
||||
|
||||
fun mergePack(list: Array<NoteState>): List<EmojiMedia> =
|
||||
list
|
||||
.mapNotNull {
|
||||
val ev = it.note.event as? EmojiPackEvent
|
||||
if (ev != null) {
|
||||
convertEmojiPack(ev)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.flatten()
|
||||
.distinctBy { it.url }
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val myEmojis by lazy {
|
||||
liveEmojiSelectionPack
|
||||
.transformLatest { emojiList ->
|
||||
if (emojiList != null) {
|
||||
emitAll(
|
||||
combineTransform(emojiList) {
|
||||
emit(mergePack(it))
|
||||
},
|
||||
)
|
||||
} else {
|
||||
emit(emptyList())
|
||||
}
|
||||
}.flowOn(Dispatchers.Default)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Eagerly,
|
||||
runBlocking(Dispatchers.Default) {
|
||||
mergePack(convertEmojiSelectionPack(getEmojiPackSelection())?.map { it.value }?.toTypedArray() ?: emptyArray())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun addPaymentRequestIfNew(paymentRequest: PaymentRequest) {
|
||||
if (
|
||||
!this.transientPaymentRequests.value.contains(paymentRequest) &&
|
||||
@ -2110,6 +2185,7 @@ class Account(
|
||||
relayList: List<RelaySetupInfo>,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String?,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -2137,6 +2213,7 @@ class Account(
|
||||
directMentions = directMentions,
|
||||
geohash = geohash,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
signer = signer,
|
||||
isDraft = draftTag != null,
|
||||
) {
|
||||
@ -2177,6 +2254,7 @@ class Account(
|
||||
relayList: List<RelaySetupInfo>,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String?,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -2199,6 +2277,7 @@ class Account(
|
||||
directMentions = directMentions,
|
||||
geohash = geohash,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
forkedFrom = forkedFrom,
|
||||
signer = signer,
|
||||
isDraft = draftTag != null,
|
||||
@ -2245,6 +2324,7 @@ class Account(
|
||||
relayList: List<RelaySetupInfo>,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String?,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -2265,6 +2345,7 @@ class Account(
|
||||
directMentions = directMentions,
|
||||
geohash = geohash,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
forkedFrom = forkedFrom,
|
||||
signer = signer,
|
||||
isDraft = draftTag != null,
|
||||
@ -2328,6 +2409,7 @@ class Account(
|
||||
directMentionsUsers: Set<User> = emptySet(),
|
||||
directMentionsNotes: Set<Note> = emptySet(),
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
wantsToMarkAsSensitive: Boolean = false,
|
||||
@ -2371,6 +2453,7 @@ class Account(
|
||||
addressesMentioned = addressesMentioned,
|
||||
eventsMentioned = eventsMentioned,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
geohash = geohash,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
@ -2403,6 +2486,7 @@ class Account(
|
||||
addressesMentioned = addressesMentioned,
|
||||
eventsMentioned = eventsMentioned,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
geohash = geohash,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
@ -2437,6 +2521,7 @@ class Account(
|
||||
directMentionsUsers: Set<User> = emptySet(),
|
||||
directMentionsNotes: Set<Note> = emptySet(),
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
wantsToMarkAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
@ -2479,6 +2564,7 @@ class Account(
|
||||
addressesMentioned = addressesMentioned,
|
||||
eventsMentioned = eventsMentioned,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
@ -2683,6 +2769,7 @@ class Account(
|
||||
relayList: List<RelaySetupInfo>,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String?,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -2705,6 +2792,7 @@ class Account(
|
||||
directMentions = directMentions,
|
||||
geohash = geohash,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
forkedFrom = forkedFrom,
|
||||
signer = signer,
|
||||
isDraft = draftTag != null,
|
||||
@ -2775,6 +2863,7 @@ class Account(
|
||||
relayList: List<RelaySetupInfo>,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String?,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -2835,6 +2924,7 @@ class Account(
|
||||
directMentions: Set<HexKey>,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String?,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -2853,6 +2943,7 @@ class Account(
|
||||
directMentions = directMentions,
|
||||
geohash = geohash,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
signer = signer,
|
||||
isDraft = draftTag != null,
|
||||
) {
|
||||
@ -2881,6 +2972,7 @@ class Account(
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String?,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -2899,6 +2991,7 @@ class Account(
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
geohash = geohash,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
signer = signer,
|
||||
isDraft = draftTag != null,
|
||||
) {
|
||||
@ -3043,6 +3136,7 @@ class Account(
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String? = null,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
@ -3061,6 +3155,7 @@ class Account(
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
geohash = geohash,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
draftTag = draftTag,
|
||||
signer = signer,
|
||||
) {
|
||||
|
@ -57,6 +57,7 @@ import com.vitorpamplona.quartz.encoders.Hex
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTagBuilder
|
||||
import com.vitorpamplona.quartz.encoders.Nip30CustomEmoji
|
||||
import com.vitorpamplona.quartz.encoders.toNpub
|
||||
import com.vitorpamplona.quartz.events.AddressableEvent
|
||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||
@ -65,6 +66,7 @@ import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.DraftEvent
|
||||
import com.vitorpamplona.quartz.events.EmojiUrl
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import com.vitorpamplona.quartz.events.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
|
||||
@ -82,7 +84,12 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.UUID
|
||||
@ -120,6 +127,27 @@ open class NewPostViewModel : ViewModel() {
|
||||
var userSuggestionAnchor: TextRange? = null
|
||||
var userSuggestionsMainMessage: UserSuggestionAnchor? = null
|
||||
|
||||
val emojiSearch: MutableStateFlow<String> = MutableStateFlow("")
|
||||
val emojiSuggestions: StateFlow<List<Account.EmojiMedia>> by lazy {
|
||||
account!!
|
||||
.myEmojis
|
||||
.combine(emojiSearch) { list, search ->
|
||||
if (search.length == 1) {
|
||||
list
|
||||
} else if (search.isNotEmpty()) {
|
||||
val code = search.removePrefix(":")
|
||||
list.filter { it.code.startsWith(code) }
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}.flowOn(Dispatchers.Default)
|
||||
.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5000),
|
||||
emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
// DMs
|
||||
var wantsDirectMessage by mutableStateOf(false)
|
||||
var toUsers by mutableStateOf(TextFieldValue(""))
|
||||
@ -196,6 +224,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
) {
|
||||
this.accountViewModel = accountViewModel
|
||||
this.account = accountViewModel.account
|
||||
this.emojiSuggestions.value
|
||||
|
||||
val noteEvent = draft?.event
|
||||
val noteAuthor = draft?.author
|
||||
@ -552,6 +581,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
val emojis = findEmoji(tagger.message, account?.myEmojis?.value)
|
||||
val urls = findURLs(tagger.message)
|
||||
val usedAttachments = iMetaAttachments.filter { it.url in urls.toSet() }
|
||||
|
||||
@ -569,6 +599,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
wantsToMarkAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
relayList = relayList,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else if (wantsExclusiveGeoPost && geoHash != null && (originalNote == null || originalNote?.event is CommentEvent)) {
|
||||
@ -583,6 +614,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
wantsToMarkAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
relayList = relayList,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else if (originalNote?.channelHex() != null) {
|
||||
@ -597,6 +629,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else {
|
||||
@ -611,6 +644,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
directMentions = tagger.directMentions,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
}
|
||||
@ -639,6 +673,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else if (!dmUsers.isNullOrEmpty()) {
|
||||
@ -654,6 +689,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else {
|
||||
@ -708,6 +744,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
relayList = relayList,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else if (originalNote?.event is TorrentCommentEvent) {
|
||||
@ -747,6 +784,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
relayList = relayList,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
}
|
||||
@ -776,6 +814,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
relayList = relayList,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else {
|
||||
@ -795,6 +834,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
relayList = relayList,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else if (wantsProduct) {
|
||||
@ -814,6 +854,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
relayList = relayList,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else {
|
||||
@ -851,12 +892,23 @@ open class NewPostViewModel : ViewModel() {
|
||||
relayList = relayList,
|
||||
geohash = geoHash,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun findEmoji(
|
||||
message: String,
|
||||
myEmojiSet: List<Account.EmojiMedia>?,
|
||||
): List<EmojiUrl> {
|
||||
if (myEmojiSet == null) return emptyList()
|
||||
return Nip30CustomEmoji.findAllEmojiCodes(message).mapNotNull { possibleEmoji ->
|
||||
myEmojiSet.firstOrNull { it.code == possibleEmoji }?.let { EmojiUrl(it.code, it.url.url) }
|
||||
}
|
||||
}
|
||||
|
||||
fun upload(
|
||||
alt: String?,
|
||||
sensitiveContent: Boolean,
|
||||
@ -978,6 +1030,10 @@ open class NewPostViewModel : ViewModel() {
|
||||
userSuggestionAnchor = null
|
||||
userSuggestionsMainMessage = null
|
||||
|
||||
if (emojiSearch.value.isNotEmpty()) {
|
||||
emojiSearch.tryEmit("")
|
||||
}
|
||||
|
||||
draftTag = UUID.randomUUID().toString()
|
||||
|
||||
NostrSearchEventOrUserDataSource.clear()
|
||||
@ -1029,6 +1085,14 @@ open class NewPostViewModel : ViewModel() {
|
||||
NostrSearchEventOrUserDataSource.clear()
|
||||
userSuggestions = emptyList()
|
||||
}
|
||||
|
||||
if (lastWord.startsWith(":")) {
|
||||
emojiSearch.tryEmit(lastWord)
|
||||
} else {
|
||||
if (emojiSearch.value.isNotBlank()) {
|
||||
emojiSearch.tryEmit("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveDraft()
|
||||
@ -1132,6 +1196,54 @@ open class NewPostViewModel : ViewModel() {
|
||||
saveDraft()
|
||||
}
|
||||
|
||||
open fun autocompleteWithEmoji(item: Account.EmojiMedia) {
|
||||
userSuggestionAnchor?.let {
|
||||
val lastWord =
|
||||
message.text
|
||||
.substring(0, it.end)
|
||||
.substringAfterLast("\n")
|
||||
.substringAfterLast(" ")
|
||||
val lastWordStart = it.end - lastWord.length
|
||||
val wordToInsert = ":${item.code}:"
|
||||
|
||||
message =
|
||||
TextFieldValue(
|
||||
message.text.replaceRange(lastWordStart, it.end, wordToInsert),
|
||||
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length),
|
||||
)
|
||||
|
||||
userSuggestionAnchor = null
|
||||
emojiSearch.tryEmit("")
|
||||
}
|
||||
|
||||
saveDraft()
|
||||
}
|
||||
|
||||
open fun autocompleteWithEmojiUrl(item: Account.EmojiMedia) {
|
||||
userSuggestionAnchor?.let {
|
||||
val lastWord =
|
||||
message.text
|
||||
.substring(0, it.end)
|
||||
.substringAfterLast("\n")
|
||||
.substringAfterLast(" ")
|
||||
val lastWordStart = it.end - lastWord.length
|
||||
val wordToInsert = item.url.url + " "
|
||||
|
||||
message =
|
||||
TextFieldValue(
|
||||
message.text.replaceRange(lastWordStart, it.end, wordToInsert),
|
||||
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length),
|
||||
)
|
||||
|
||||
userSuggestionAnchor = null
|
||||
emojiSearch.tryEmit("")
|
||||
}
|
||||
|
||||
urlPreview = findUrlInMessage()
|
||||
|
||||
saveDraft()
|
||||
}
|
||||
|
||||
private fun newStateMapPollOptions(): SnapshotStateMap<Int, String> = mutableStateMapOf(Pair(0, ""), Pair(1, ""))
|
||||
|
||||
fun canPost(): Boolean =
|
||||
|
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.content.MediaType.Companion.Text
|
||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.OpenInFull
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.map
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.UrlImageView
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Composable
|
||||
fun WatchAndLoadMyEmojiList(accountViewModel: AccountViewModel) {
|
||||
LoadAddressableNote(
|
||||
EmojiPackSelectionEvent.createAddressATag(accountViewModel.userProfile().pubkeyHex),
|
||||
accountViewModel,
|
||||
) { emptyNote ->
|
||||
emptyNote?.let { usersEmojiList ->
|
||||
val collections by usersEmojiList
|
||||
.live()
|
||||
.metadata
|
||||
.map { (it.note.event as? EmojiPackSelectionEvent)?.taggedAddresses()?.toImmutableList() }
|
||||
.distinctUntilChanged()
|
||||
.observeAsState((usersEmojiList.event as? EmojiPackSelectionEvent)?.taggedAddresses()?.toImmutableList())
|
||||
|
||||
collections?.forEach {
|
||||
LoadAddressableNote(aTag = it, accountViewModel) {
|
||||
it?.live()?.metadata?.observeAsState()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShowEmojiSuggestionList(
|
||||
emojiSuggestions: Flow<List<Account.EmojiMedia>>,
|
||||
onSelect: (Account.EmojiMedia) -> Unit,
|
||||
onFullSize: (Account.EmojiMedia) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
modifier: Modifier = Modifier.heightIn(0.dp, 200.dp),
|
||||
) {
|
||||
val suggestions by emojiSuggestions.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
if (suggestions.isNotEmpty()) {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(top = 10.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
items(suggestions) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.clickable { onSelect(it) }.padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = 10.dp,
|
||||
bottom = 10.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = spacedBy(Size10dp),
|
||||
) {
|
||||
Box(Modifier.size(40.dp)) {
|
||||
UrlImageView(it.url, accountViewModel)
|
||||
}
|
||||
Text(it.code, fontWeight = FontWeight.Bold, modifier = Modifier.weight(1f))
|
||||
Box(Modifier.size(40.dp), contentAlignment = Alignment.Center) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onFullSize(it)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.OpenInFull,
|
||||
contentDescription = stringRes(R.string.use_direct_url),
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -144,7 +144,6 @@ class UpdateReactionTypeViewModel : ViewModel() {
|
||||
?.value
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun UpdateReactionTypeDialog(
|
||||
onClose: () -> Unit,
|
||||
|
@ -157,8 +157,10 @@ import com.vitorpamplona.amethyst.ui.note.LoadCityName
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.PollIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.RegularPostIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.ShowEmojiSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.note.ShowUserSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.WatchAndLoadMyEmojiList
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapSplitIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.MyTextField
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
|
||||
@ -209,6 +211,7 @@ fun NewPostScreen(
|
||||
nav: Nav,
|
||||
) {
|
||||
val postViewModel: NewPostViewModel = viewModel()
|
||||
postViewModel.account = accountViewModel.account
|
||||
postViewModel.wantsDirectMessage = enableMessageInterface
|
||||
postViewModel.wantsToAddGeoHash = enableGeolocation
|
||||
|
||||
@ -278,6 +281,8 @@ fun NewPostScreen(
|
||||
onDispose { activity.removeOnNewIntentListener(consumer) }
|
||||
}
|
||||
|
||||
WatchAndLoadMyEmojiList(accountViewModel)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
@ -575,6 +580,14 @@ fun NewPostScreen(
|
||||
modifier = Modifier.heightIn(0.dp, 300.dp),
|
||||
)
|
||||
|
||||
ShowEmojiSuggestionList(
|
||||
postViewModel.emojiSuggestions,
|
||||
postViewModel::autocompleteWithEmoji,
|
||||
postViewModel::autocompleteWithEmojiUrl,
|
||||
accountViewModel,
|
||||
modifier = Modifier.heightIn(0.dp, 300.dp),
|
||||
)
|
||||
|
||||
BottomRowActions(postViewModel)
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,7 @@ import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadChannel
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteUsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.ShowEmojiSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.note.ShowUserSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
@ -397,6 +398,7 @@ private suspend fun innerSendPost(
|
||||
|
||||
val urls = findURLs(tagger.message)
|
||||
val usedAttachments = newPostModel.iMetaAttachments.filter { it.url in urls.toSet() }
|
||||
val emojis = newPostModel.findEmoji(newPostModel.message.text, accountViewModel.account.myEmojis.value)
|
||||
|
||||
if (channel is PublicChatChannel) {
|
||||
accountViewModel.account.sendChannelMessage(
|
||||
@ -407,6 +409,7 @@ private suspend fun innerSendPost(
|
||||
directMentions = tagger.directMentions,
|
||||
wantsToMarkAsSensitive = false,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = draftTag,
|
||||
)
|
||||
} else if (channel is LiveActivitiesChannel) {
|
||||
@ -417,6 +420,7 @@ private suspend fun innerSendPost(
|
||||
mentions = tagger.pTags,
|
||||
wantsToMarkAsSensitive = false,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = draftTag,
|
||||
)
|
||||
}
|
||||
@ -485,6 +489,13 @@ fun EditFieldRow(
|
||||
accountViewModel,
|
||||
)
|
||||
|
||||
ShowEmojiSuggestionList(
|
||||
channelScreenModel.emojiSuggestions,
|
||||
channelScreenModel::autocompleteWithEmoji,
|
||||
channelScreenModel::autocompleteWithEmojiUrl,
|
||||
accountViewModel,
|
||||
)
|
||||
|
||||
MyTextField(
|
||||
value = channelScreenModel.message,
|
||||
onValueChange = { channelScreenModel.updateMessage(it) },
|
||||
|
@ -95,6 +95,7 @@ import com.vitorpamplona.amethyst.ui.note.IncognitoIconOff
|
||||
import com.vitorpamplona.amethyst.ui.note.IncognitoIconOn
|
||||
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
||||
import com.vitorpamplona.amethyst.ui.note.QuickActionAlertDialog
|
||||
import com.vitorpamplona.amethyst.ui.note.ShowEmojiSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.note.ShowUserSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
@ -517,6 +518,7 @@ private fun innerSendPost(
|
||||
) {
|
||||
val urls = findURLs(newPostModel.message.text)
|
||||
val usedAttachments = newPostModel.iMetaAttachments.filter { it.url !in urls.toSet() }
|
||||
val emojis = newPostModel.findEmoji(newPostModel.message.text, accountViewModel.account.myEmojis.value)
|
||||
|
||||
if (newPostModel.nip17 || room.users.size > 1 || replyTo.value?.event is NIP17Group) {
|
||||
accountViewModel.account.sendNIP17PrivateMessage(
|
||||
@ -526,6 +528,7 @@ private fun innerSendPost(
|
||||
mentions = null,
|
||||
wantsToMarkAsSensitive = false,
|
||||
imetas = usedAttachments,
|
||||
emojis = emojis,
|
||||
draftTag = dTag,
|
||||
)
|
||||
} else {
|
||||
@ -557,6 +560,13 @@ fun PrivateMessageEditFieldRow(
|
||||
accountViewModel,
|
||||
)
|
||||
|
||||
ShowEmojiSuggestionList(
|
||||
channelScreenModel.emojiSuggestions,
|
||||
channelScreenModel::autocompleteWithEmoji,
|
||||
channelScreenModel::autocompleteWithEmojiUrl,
|
||||
accountViewModel,
|
||||
)
|
||||
|
||||
MyTextField(
|
||||
value = channelScreenModel.message,
|
||||
onValueChange = { channelScreenModel.updateMessage(it) },
|
||||
|
@ -377,6 +377,8 @@
|
||||
<string name="add_caption">Add a Caption</string>
|
||||
<string name="add_caption_example">My lovely friend</string>
|
||||
|
||||
<string name="use_direct_url">Use direct URL</string>
|
||||
|
||||
<string name="content_description">Description of the contents</string>
|
||||
<string name="content_description_example">A blue boat in a white sandy beach at sunset</string>
|
||||
|
||||
|
@ -50,6 +50,24 @@ class Nip30CustomEmoji {
|
||||
|
||||
fun createEmojiMap(tags: ImmutableListOfLists<String>): Map<String, String> = tags.lists.filter { it.size > 2 && it[0] == "emoji" }.associate { ":${it[1]}:" to it[2] }
|
||||
|
||||
fun findAllEmojis(input: String): List<String> {
|
||||
val matcher = customEmojiPattern.matcher(input)
|
||||
val emojiNamesInOrder = mutableListOf<String>()
|
||||
while (matcher.find()) {
|
||||
emojiNamesInOrder.add(matcher.group())
|
||||
}
|
||||
return emojiNamesInOrder
|
||||
}
|
||||
|
||||
fun findAllEmojiCodes(input: String): List<String> {
|
||||
val matcher = customEmojiPattern.matcher(input)
|
||||
val emojiNamesInOrder = mutableListOf<String>()
|
||||
while (matcher.find()) {
|
||||
matcher.group(1)?.let { emojiNamesInOrder.add(it) }
|
||||
}
|
||||
return emojiNamesInOrder
|
||||
}
|
||||
|
||||
fun assembleAnnotatedList(
|
||||
input: String,
|
||||
allTags: ImmutableListOfLists<String>?,
|
||||
@ -65,12 +83,7 @@ class Nip30CustomEmoji {
|
||||
input: String,
|
||||
emojiPairs: Map<String, String>,
|
||||
): ImmutableList<Renderable>? {
|
||||
val matcher = customEmojiPattern.matcher(input)
|
||||
val emojiNamesInOrder = mutableListOf<String>()
|
||||
while (matcher.find()) {
|
||||
emojiNamesInOrder.add(matcher.group())
|
||||
}
|
||||
|
||||
val emojiNamesInOrder = findAllEmojis(input)
|
||||
if (emojiNamesInOrder.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ class ChannelMessageEvent(
|
||||
directMentions: Set<HexKey> = emptySet(),
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (ChannelMessageEvent) -> Unit,
|
||||
) {
|
||||
@ -88,6 +89,7 @@ class ChannelMessageEvent(
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
tags.add(
|
||||
arrayOf("alt", ALT),
|
||||
)
|
||||
|
@ -84,6 +84,7 @@ class ChatMessageEvent(
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (ChatMessageEvent) -> Unit,
|
||||
) {
|
||||
@ -103,6 +104,7 @@ class ChatMessageEvent(
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
// tags.add(arrayOf("alt", alt))
|
||||
|
||||
if (isDraft) {
|
||||
|
@ -115,6 +115,7 @@ class ClassifiedsEvent(
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
isDraft: Boolean,
|
||||
@ -190,9 +191,8 @@ class ClassifiedsEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
imetas?.forEach { tags.add(Nip92MediaAttachments.createTag(it)) }
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
if (isDraft) {
|
||||
|
@ -98,6 +98,7 @@ class CommentEvent(
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
@ -120,7 +121,7 @@ class CommentEvent(
|
||||
tags.add(removeTrailingNullsAndEmptyOthers("e", replyingTo.event.id, replyingTo.relay, replyingTo.event.pubKey))
|
||||
tags.add(arrayOf("k", "${replyingTo.event.kind}"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, emojis, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
fun replyComment(
|
||||
@ -130,6 +131,7 @@ class CommentEvent(
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
@ -147,7 +149,7 @@ class CommentEvent(
|
||||
tags.add(removeTrailingNullsAndEmptyOthers("e", replyingTo.event.id, replyingTo.relay, replyingTo.event.pubKey))
|
||||
tags.add(arrayOf("k", "${replyingTo.event.kind}"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, emojis, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
fun createGeoComment(
|
||||
@ -157,6 +159,7 @@ class CommentEvent(
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
@ -169,7 +172,7 @@ class CommentEvent(
|
||||
geohash?.let { tags.addAll(rootGeohashMipMap(it)) }
|
||||
tags.add(arrayOf("K", "geo"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, null, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, emojis, null, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
private fun create(
|
||||
@ -179,6 +182,7 @@ class CommentEvent(
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
@ -202,6 +206,8 @@ class CommentEvent(
|
||||
|
||||
findURLs(msg).forEach { tags.add(arrayOf("r", it)) }
|
||||
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
|
||||
zapReceiver?.forEach {
|
||||
tags.add(arrayOf("zap", it.lnAddressOrPubKeyHex, it.relay ?: "", it.weight.toString()))
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ data class EmojiUrl(
|
||||
) {
|
||||
fun encode(): String = ":$code:$url"
|
||||
|
||||
fun toTagArray() = arrayOf("emoji", code, url)
|
||||
|
||||
companion object {
|
||||
fun decode(encodedEmojiSetup: String): EmojiUrl? {
|
||||
val emojiParts = encodedEmojiSetup.split(":", limit = 3)
|
||||
@ -71,5 +73,12 @@ data class EmojiUrl(
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(tag: Array<String>): EmojiUrl? =
|
||||
if (tag.size > 2 && tag[0] == "emoji") {
|
||||
EmojiUrl(tag[1], tag[2])
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,10 @@ class EmojiPackSelectionEvent(
|
||||
const val FIXED_D_TAG = ""
|
||||
const val ALT = "Emoji selection"
|
||||
|
||||
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, AdvertisedRelayListEvent.FIXED_D_TAG, null)
|
||||
|
||||
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, AdvertisedRelayListEvent.FIXED_D_TAG)
|
||||
|
||||
fun create(
|
||||
listOfEmojiPacks: List<ATag>?,
|
||||
signer: NostrSigner,
|
||||
|
@ -164,7 +164,7 @@ open class Event(
|
||||
ATag.parse(aTagValue, relay)
|
||||
}
|
||||
|
||||
override fun taggedEmojis() = tags.filter { it.size > 2 && it[0] == "emoji" }.map { EmojiUrl(it[1], it[2]) }
|
||||
override fun taggedEmojis() = tags.filter { it.size > 2 && it[0] == "emoji" }.mapNotNull { EmojiUrl.parse(it) }
|
||||
|
||||
override fun isSensitive() =
|
||||
tags.any {
|
||||
|
@ -92,6 +92,7 @@ class GitReplyEvent(
|
||||
directMentions: Set<HexKey> = emptySet(),
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
forkedFrom: Event? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
@ -152,6 +153,7 @@ class GitReplyEvent(
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
tags.add(arrayOf("alt", "a git issue reply"))
|
||||
|
||||
if (isDraft) {
|
||||
|
@ -53,6 +53,7 @@ open class InteractiveStoryBaseEvent(
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
): Array<Array<String>> {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
findHashtags(content).forEach {
|
||||
@ -75,6 +76,7 @@ open class InteractiveStoryBaseEvent(
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
return tags.toTypedArray()
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,7 @@ class LiveActivitiesChatMessageEvent(
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (LiveActivitiesChatMessageEvent) -> Unit,
|
||||
) {
|
||||
@ -96,6 +97,7 @@ class LiveActivitiesChatMessageEvent(
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
if (isDraft) {
|
||||
|
@ -82,6 +82,7 @@ class NIP17Factory {
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
draftTag: String? = null,
|
||||
onReady: (Result) -> Unit,
|
||||
) {
|
||||
@ -100,6 +101,7 @@ class NIP17Factory {
|
||||
geohash = geohash,
|
||||
isDraft = draftTag != null,
|
||||
imetas = imetas,
|
||||
emojis = emojis,
|
||||
) { senderMessage ->
|
||||
if (draftTag != null) {
|
||||
onReady(
|
||||
|
@ -81,6 +81,7 @@ class PollNoteEvent(
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (PollNoteEvent) -> Unit,
|
||||
) {
|
||||
@ -108,6 +109,7 @@ class PollNoteEvent(
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
if (isDraft) {
|
||||
|
@ -58,6 +58,7 @@ class TextNoteEvent(
|
||||
directMentions: Set<HexKey> = emptySet(),
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
forkedFrom: Event? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
@ -123,9 +124,8 @@ class TextNoteEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
imetas?.forEach { tags.add(Nip92MediaAttachments.createTag(it)) }
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
|
||||
if (isDraft) {
|
||||
signer.assembleRumor(createdAt, KIND, tags.toTypedArray(), msg, onReady)
|
||||
|
@ -63,6 +63,7 @@ class TorrentCommentEvent(
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
forkedFrom: Event? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (TorrentCommentEvent) -> Unit,
|
||||
@ -126,6 +127,7 @@ class TorrentCommentEvent(
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
if (isDraft) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user