From aa4456071400f4178451063ceeacc58f5e8b709a Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 3 Jan 2025 20:29:31 -0500 Subject: [PATCH] Adds support for Custom Reaction selection on new posts and direct GIF url usage. --- .../vitorpamplona/amethyst/model/Account.kt | 95 ++++++++++++ .../amethyst/ui/actions/NewPostViewModel.kt | 112 +++++++++++++++ .../ui/note/ShowEmojiSuggestionList.kt | 136 ++++++++++++++++++ .../ui/note/UpdateReactionTypeDialog.kt | 1 - .../ui/screen/loggedIn/NewPostScreen.kt | 13 ++ .../loggedIn/chatrooms/ChannelScreen.kt | 11 ++ .../loggedIn/chatrooms/ChatroomScreen.kt | 10 ++ amethyst/src/main/res/values/strings.xml | 2 + .../quartz/encoders/Nip30CustomEmoji.kt | 25 +++- .../quartz/events/ChannelMessageEvent.kt | 2 + .../quartz/events/ChatMessageEvent.kt | 2 + .../quartz/events/ClassifiedsEvent.kt | 6 +- .../quartz/events/CommentEvent.kt | 12 +- .../quartz/events/EmojiPackEvent.kt | 9 ++ .../quartz/events/EmojiPackSelectionEvent.kt | 4 + .../com/vitorpamplona/quartz/events/Event.kt | 2 +- .../quartz/events/GitReplyEvent.kt | 2 + .../events/InteractiveStoryBaseEvent.kt | 2 + .../events/LiveActivitiesChatMessageEvent.kt | 2 + .../quartz/events/NIP17Factory.kt | 2 + .../quartz/events/PollNoteEvent.kt | 2 + .../quartz/events/TextNoteEvent.kt | 6 +- .../quartz/events/TorrentCommentEvent.kt | 2 + 23 files changed, 443 insertions(+), 17 deletions(-) create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ShowEmojiSuggestionList.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 55be95cbf..85fac791a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -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 = getEmojiPackSelectionNote().flow().metadata.stateFlow + + fun getEmojiPackSelectionNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(EmojiPackSelectionEvent.createAddressATag(userProfile().pubkeyHex)) + + fun convertEmojiSelectionPack(selection: EmojiPackSelectionEvent?): List>? = + selection?.taggedAddresses()?.map { + LocalCache + .getOrCreateAddressableNote(it) + .flow() + .metadata.stateFlow + } + + @OptIn(ExperimentalCoroutinesApi::class) + val liveEmojiSelectionPack: StateFlow>?> 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 = + pack.taggedEmojis().map { + EmojiMedia(it.code, MediaUrlImage(it.url)) + } + + fun mergePack(list: Array): List = + 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, geohash: String? = null, imetas: List? = null, + emojis: List? = 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, geohash: String? = null, imetas: List? = null, + emojis: List? = 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, geohash: String? = null, imetas: List? = null, + emojis: List? = 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 = emptySet(), directMentionsNotes: Set = emptySet(), imetas: List? = null, + emojis: List? = null, geohash: String? = null, zapReceiver: List? = 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 = emptySet(), directMentionsNotes: Set = emptySet(), imetas: List? = null, + emojis: List? = null, zapReceiver: List? = 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, geohash: String? = null, imetas: List? = null, + emojis: List? = 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, geohash: String? = null, imetas: List? = null, + emojis: List? = null, draftTag: String?, ) { if (!isWriteable()) return @@ -2835,6 +2924,7 @@ class Account( directMentions: Set, geohash: String? = null, imetas: List? = null, + emojis: List? = 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? = null, + emojis: List? = 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? = null, + emojis: List? = 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, ) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index e16232d9e..9ce1f1edf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -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 = MutableStateFlow("") + val emojiSuggestions: StateFlow> 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?, + ): List { + 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 = mutableStateMapOf(Pair(0, ""), Pair(1, "")) fun canPost(): Boolean = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ShowEmojiSuggestionList.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ShowEmojiSuggestionList.kt new file mode 100644 index 000000000..2a6b21edf --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ShowEmojiSuggestionList.kt @@ -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>, + 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, + ) + } + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt index 0333d2085..bcb40a371 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt @@ -144,7 +144,6 @@ class UpdateReactionTypeViewModel : ViewModel() { ?.value } -@OptIn(ExperimentalLayoutApi::class) @Composable fun UpdateReactionTypeDialog( onClose: () -> Unit, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt index 8f93f0704..b9ed00a23 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt @@ -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) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChannelScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChannelScreen.kt index 77b4f32b9..74f744f1f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChannelScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChannelScreen.kt @@ -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) }, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomScreen.kt index d7edd0872..31cddc896 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomScreen.kt @@ -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) }, diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index af6cef189..c779f88a8 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -377,6 +377,8 @@ Add a Caption My lovely friend + Use direct URL + Description of the contents A blue boat in a white sandy beach at sunset diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Nip30CustomEmoji.kt b/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Nip30CustomEmoji.kt index 40fd2cf03..de5823ab9 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Nip30CustomEmoji.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/encoders/Nip30CustomEmoji.kt @@ -50,6 +50,24 @@ class Nip30CustomEmoji { fun createEmojiMap(tags: ImmutableListOfLists): Map = tags.lists.filter { it.size > 2 && it[0] == "emoji" }.associate { ":${it[1]}:" to it[2] } + fun findAllEmojis(input: String): List { + val matcher = customEmojiPattern.matcher(input) + val emojiNamesInOrder = mutableListOf() + while (matcher.find()) { + emojiNamesInOrder.add(matcher.group()) + } + return emojiNamesInOrder + } + + fun findAllEmojiCodes(input: String): List { + val matcher = customEmojiPattern.matcher(input) + val emojiNamesInOrder = mutableListOf() + while (matcher.find()) { + matcher.group(1)?.let { emojiNamesInOrder.add(it) } + } + return emojiNamesInOrder + } + fun assembleAnnotatedList( input: String, allTags: ImmutableListOfLists?, @@ -65,12 +83,7 @@ class Nip30CustomEmoji { input: String, emojiPairs: Map, ): ImmutableList? { - val matcher = customEmojiPattern.matcher(input) - val emojiNamesInOrder = mutableListOf() - while (matcher.find()) { - emojiNamesInOrder.add(matcher.group()) - } - + val emojiNamesInOrder = findAllEmojis(input) if (emojiNamesInOrder.isEmpty()) { return null } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelMessageEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelMessageEvent.kt index ca455cf00..38da37023 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelMessageEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelMessageEvent.kt @@ -62,6 +62,7 @@ class ChannelMessageEvent( directMentions: Set = emptySet(), geohash: String? = null, imetas: List? = null, + emojis: List? = 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), ) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/ChatMessageEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/ChatMessageEvent.kt index 73925dda5..1af3a6b6e 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/ChatMessageEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/ChatMessageEvent.kt @@ -84,6 +84,7 @@ class ChatMessageEvent( signer: NostrSigner, createdAt: Long = TimeUtils.now(), imetas: List? = null, + emojis: List? = 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) { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/ClassifiedsEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/ClassifiedsEvent.kt index 5a1aadf6b..b9c4522a9 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/ClassifiedsEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/ClassifiedsEvent.kt @@ -115,6 +115,7 @@ class ClassifiedsEvent( zapRaiserAmount: Long?, geohash: String? = null, imetas: List? = null, + emojis: List? = 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) { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/CommentEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/CommentEvent.kt index 2e8e4c509..7481019f8 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/CommentEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/CommentEvent.kt @@ -98,6 +98,7 @@ class CommentEvent( addressesMentioned: Set = emptySet(), eventsMentioned: Set = emptySet(), imetas: List? = null, + emojis: List? = null, geohash: String? = null, zapReceiver: List? = 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 = emptySet(), eventsMentioned: Set = emptySet(), imetas: List? = null, + emojis: List? = null, geohash: String? = null, zapReceiver: List? = 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 = emptySet(), eventsMentioned: Set = emptySet(), imetas: List? = null, + emojis: List? = null, zapReceiver: List? = 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 = emptySet(), eventsMentioned: Set = emptySet(), imetas: List? = null, + emojis: List? = null, geohash: String? = null, zapReceiver: List? = 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())) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackEvent.kt index 0599fc9b8..887f24416 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackEvent.kt @@ -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): EmojiUrl? = + if (tag.size > 2 && tag[0] == "emoji") { + EmojiUrl(tag[1], tag[2]) + } else { + null + } } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackSelectionEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackSelectionEvent.kt index b8b449ba3..ab758aa34 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackSelectionEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/EmojiPackSelectionEvent.kt @@ -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?, signer: NostrSigner, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt index e78ac2beb..a91b82bad 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt @@ -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 { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/GitReplyEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/GitReplyEvent.kt index 1757be562..3b40d605f 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/GitReplyEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/GitReplyEvent.kt @@ -92,6 +92,7 @@ class GitReplyEvent( directMentions: Set = emptySet(), geohash: String? = null, imetas: List? = null, + emojis: List? = 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) { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/InteractiveStoryBaseEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/InteractiveStoryBaseEvent.kt index bda994aed..4c865724d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/InteractiveStoryBaseEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/InteractiveStoryBaseEvent.kt @@ -53,6 +53,7 @@ open class InteractiveStoryBaseEvent( zapRaiserAmount: Long? = null, geohash: String? = null, imetas: List? = null, + emojis: List? = null, ): Array> { val tags = mutableListOf>() 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() } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesChatMessageEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesChatMessageEvent.kt index e600a6b87..afee128cb 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesChatMessageEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesChatMessageEvent.kt @@ -75,6 +75,7 @@ class LiveActivitiesChatMessageEvent( zapRaiserAmount: Long?, geohash: String? = null, imetas: List? = null, + emojis: List? = 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) { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP17Factory.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP17Factory.kt index f3d8886c8..7f2715d29 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP17Factory.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP17Factory.kt @@ -82,6 +82,7 @@ class NIP17Factory { zapRaiserAmount: Long? = null, geohash: String? = null, imetas: List? = null, + emojis: List? = 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( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt index 5be47b30d..cb95c5d1f 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/PollNoteEvent.kt @@ -81,6 +81,7 @@ class PollNoteEvent( zapRaiserAmount: Long?, geohash: String? = null, imetas: List? = null, + emojis: List? = 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) { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt index 212307010..0ed34f369 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt @@ -58,6 +58,7 @@ class TextNoteEvent( directMentions: Set = emptySet(), geohash: String? = null, imetas: List? = null, + emojis: List? = 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) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/TorrentCommentEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/TorrentCommentEvent.kt index 21b751b50..1513be3e6 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/TorrentCommentEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/TorrentCommentEvent.kt @@ -63,6 +63,7 @@ class TorrentCommentEvent( zapRaiserAmount: Long?, geohash: String? = null, imetas: List? = null, + emojis: List? = 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) {