From ad53bff8e3f7a3064af4e999a4350dbbc045ce69 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 19 Mar 2026 15:23:48 -0400 Subject: [PATCH] Final touches to remove nip04 dms --- .../loggedIn/chats/privateDM/ChatroomView.kt | 14 +- .../privateDM/send/ChatNewMessageViewModel.kt | 109 ++++++++---- .../chats/privateDM/send/NewGroupDMScreen.kt | 17 +- .../send/PrivateMessageEditFieldRow.kt | 164 +++++++++++++----- .../privateDM/send/upload/ChatFileSender.kt | 35 ---- .../send/upload/ChatFileUploadDialog.kt | 6 +- amethyst/src/main/res/values/strings.xml | 2 +- 7 files changed, 222 insertions(+), 125 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt index 34709b437..358247a94 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomView.kt @@ -26,14 +26,16 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.navigation.navs.INav -import com.vitorpamplona.amethyst.ui.note.elements.ObserveRelayListForDMs +import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.note.elements.ObserveRelayListForDMsAndDisplayIfNotFound import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.RefreshingChatroomFeedView @@ -44,6 +46,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.Privat import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey +import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import kotlinx.coroutines.launch @Composable @@ -94,8 +97,13 @@ fun ChatroomView( // Reactively check if recipients have DM relays for NIP-17 delivery for (userHex in room.users) { - ObserveRelayListForDMs(pubkey = userHex, accountViewModel = accountViewModel) { - newPostModel.updateRecipientRelayStatus() + LoadAddressableNote( + ChatMessageRelayListEvent.createAddress(userHex), + accountViewModel, + ) { note -> + if (note != null) { + EventFinderFilterAssemblerSubscription(note, accountViewModel) + } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt index 4e80558b1..1d9c4dcbc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt @@ -73,6 +73,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.base.NIP17Group import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent +import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes import com.vitorpamplona.quartz.nip19Bech32.toNpub import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji @@ -93,10 +94,24 @@ import com.vitorpamplona.quartz.utils.Hex import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.TimeUtils import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch @Stable @@ -125,10 +140,57 @@ class ChatNewMessageViewModel : } } - var room: ChatroomKey? by mutableStateOf(null) + val room = MutableStateFlow(null) - /** Whether any recipients are missing DM relay lists */ - var recipientsMissingDmRelays by mutableStateOf(false) + val roomUsers: StateFlow> = + room + .mapNotNull { + it?.users?.mapNotNull { userHex -> LocalCache.checkGetOrCreateUser(userHex) } ?: emptyList() + }.flowOn(Dispatchers.IO) + .stateIn( + viewModelScope, + SharingStarted.Eagerly, + room.value?.users?.mapNotNull { userHex -> LocalCache.checkGetOrCreateUser(userHex) } ?: emptyList(), + ) + + @OptIn(ExperimentalCoroutinesApi::class) + val recipientsMissingDmRelays: StateFlow> = + roomUsers + .transformLatest { + val dmRelayListNoteFlows = + it.map { user -> + user.dmRelayListNote + .flow() + .metadata.stateFlow + } + + if (dmRelayListNoteFlows.isEmpty()) { + emitAll(MutableStateFlow(persistentListOf())) + } else { + val flow = + combine(dmRelayListNoteFlows) { dmRelayListNotes -> + dmRelayListNotes + .mapNotNull { noteState -> + val noteEvent = noteState.note.event as? ChatMessageRelayListEvent + if (noteEvent == null || noteEvent.relays().isEmpty()) { + noteState.note.author + } else { + null + } + }.toImmutableList() + } + emitAll(flow) + } + }.onStart { + }.onCompletion { + }.stateIn( + viewModelScope, + SharingStarted.Eagerly, + roomUsers.value + .filter { user -> + user.dmInboxRelays().isNullOrEmpty() + }.toImmutableList(), + ) val replyTo = mutableStateOf(null) @@ -180,9 +242,6 @@ class ChatNewMessageViewModel : var wantsZapraiser by mutableStateOf(false) override var zapRaiserAmount = mutableStateOf(null) - // NIP17 is always enabled - NIP-04 is deprecated for sending - val nip17: Boolean get() = true - fun lnAddress(): String? = account.userProfile().lnAddress() fun hasLnAddress(): Boolean = account.userProfile().lnAddress() != null @@ -208,26 +267,11 @@ class ChatNewMessageViewModel : } fun load(room: ChatroomKey) { - this.room = room + this.room.tryEmit(room) this.toUsers = TextFieldValue( room.users.mapNotNull { runCatching { Hex.decode(it).toNpub() }.getOrNull() }.joinToString(", ") { "@$it" }, ) - - updateRecipientRelayStatus() - } - - fun updateRecipientRelayStatus() { - val room = this.room - if (room != null) { - this.recipientsMissingDmRelays = - room.users.any { hexKey -> - val user = LocalCache.getOrCreateUser(hexKey) - user.dmInboxRelays().isNullOrEmpty() - } - } else { - this.recipientsMissingDmRelays = false - } } fun reply(replyNote: Note) { @@ -360,8 +404,6 @@ class ChatNewMessageViewModel : urlPreviews.update(message) iMetaAttachments.addAll(draftEvent.imetas()) - - updateRecipientRelayStatus() } suspend fun sendPostSync() { @@ -423,7 +465,7 @@ class ChatNewMessageViewModel : context: Context, onceUploaded: () -> Unit, ) { - val room = room ?: return + val room = room.value ?: return val uploadState = uploadState ?: return accountViewModel.launchSigner { @@ -471,12 +513,12 @@ class ChatNewMessageViewModel : val onError = pendingRetryOnError val context = pendingRetryContext val onceUploaded = pendingRetryOnceUploaded - val room = room + val room = room.value val uploadState = uploadState dismissEncryptedUploadError() - if (uploadState == null || context == null) return + if (room == null || uploadState == null || context == null) return uploadState.encryptFiles = false @@ -495,7 +537,6 @@ class ChatNewMessageViewModel : } RetryMode.SEND -> { - if (room == null) return@launchSigner ChatFileUploader(account).justUploadNIP17Unencrypted( uploadState, onError ?: accountViewModel.toastManager::toast, @@ -511,7 +552,7 @@ class ChatNewMessageViewModel : } private suspend fun innerSendPost(draftTag: String?) { - val room = room ?: return + val room = room.value ?: return val urls = findURLs(message.text) val usedAttachments = iMetaAttachments.filterIsIn(urls.toSet()) @@ -661,12 +702,10 @@ class ChatNewMessageViewModel : val users = toUsersTagger.pTags?.mapTo(mutableSetOf()) { it.pubkeyHex } if (users.isNullOrEmpty()) { - room = null - updateRecipientRelayStatus() + room.emit(null) } else { - if (users != room?.users) { - room = ChatroomKey(users) - updateRecipientRelayStatus() + if (users != room.value?.users) { + room.emit(ChatroomKey(users)) } } } @@ -743,7 +782,7 @@ class ChatNewMessageViewModel : (!wantsZapraiser || zapRaiserAmount.value != null) && (toUsers.text.isNotBlank()) && uploadState?.multiOrchestrator == null && - !recipientsMissingDmRelays + recipientsMissingDmRelays.value.isEmpty() fun insertAtCursor(newElement: String) { message = message.insertUrlAtCursor(newElement) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt index 8b815f3c2..ac194c757 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt @@ -197,7 +197,7 @@ fun NewGroupDMScreen( // function when the postViewModel is released accountViewModel.launchSigner { postViewModel.sendPostSync() - postViewModel.room?.let { + postViewModel.room.value?.let { nav.nav(routeToMessage(it, null, null, null, null, accountViewModel)) } } @@ -332,8 +332,9 @@ fun GroupDMScreenContent( ) } - if (postViewModel.recipientsMissingDmRelays) { - RecipientMissingRelaysWarning() + val missingRelays by postViewModel.recipientsMissingDmRelays.collectAsStateWithLifecycle() + if (missingRelays.isNotEmpty()) { + RecipientMissingRelaysWarning(missingRelays, accountViewModel, nav) } BottomRowActions(postViewModel, accountViewModel) @@ -397,7 +398,9 @@ private fun BottomRowActions( .height(50.dp), verticalAlignment = CenterVertically, ) { - if (postViewModel.room != null) { + val room by postViewModel.room.collectAsStateWithLifecycle() + + if (room != null) { SelectFromGallery( isUploading = postViewModel.isUploadingImage, enabled = !postViewModel.isUploadingFile, @@ -428,7 +431,7 @@ private fun BottomRowActions( } } - if (postViewModel.room != null) { + if (room != null) { TakePictureButton( onPictureTaken = { postViewModel.pickedMedia(it) }, ) @@ -447,7 +450,7 @@ private fun BottomRowActions( } } - if (postViewModel.room != null) { + if (room != null) { TakeVideoButton( onVideoTaken = { postViewModel.pickedMedia(it) }, ) @@ -554,8 +557,6 @@ fun SendDirectMessageTo( focusedBorderColor = Color.Transparent, ), ) - - Nip17Indicator(postViewModel) } HorizontalDivider(thickness = DividerThickness) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt index 65436e43f..57cdd853f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt @@ -20,35 +20,50 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send -import android.R.attr.maxLines import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.input.InputTransformation.Companion.keyboardOptions import androidx.compose.material3.AlertDialog import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.key import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor +import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.ShowEmojiSuggestionList import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList +import com.vitorpamplona.amethyst.ui.note.showCount import com.vitorpamplona.amethyst.ui.note.timeAheadNoDot import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.RoomChatFileUploadDialog @@ -61,9 +76,13 @@ import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier import com.vitorpamplona.amethyst.ui.theme.Font12SP import com.vitorpamplona.amethyst.ui.theme.PostKeyboard +import com.vitorpamplona.amethyst.ui.theme.Size25dp +import com.vitorpamplona.amethyst.ui.theme.SpacedBy10dp import com.vitorpamplona.amethyst.ui.theme.SuggestionListDefaultHeightChat import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.ui.theme.placeholderText +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf @Preview @Composable @@ -164,7 +183,12 @@ fun PrivateMessageEditFieldRow( } } - EditField(channelScreenModel, onSendNewMessage, accountViewModel) + val missingRelays by channelScreenModel.recipientsMissingDmRelays.collectAsStateWithLifecycle() + if (missingRelays.isNotEmpty()) { + RecipientMissingRelaysWarning(missingRelays, accountViewModel, nav) + } else { + EditField(channelScreenModel, onSendNewMessage, accountViewModel) + } } } @@ -174,55 +198,81 @@ fun EditField( onSendNewMessage: () -> Unit, accountViewModel: AccountViewModel, ) { - if (channelScreenModel.recipientsMissingDmRelays) { - RecipientMissingRelaysWarning() - } else { - ThinPaddingTextField( - value = channelScreenModel.message, - onValueChange = { channelScreenModel.updateMessage(it) }, - keyboardOptions = PostKeyboard, - shape = EditFieldBorder, - modifier = Modifier.fillMaxWidth(), - placeholder = { - Text( - text = stringRes(R.string.reply_here), - color = MaterialTheme.colorScheme.placeholderText, - ) - }, - trailingIcon = { - ThinSendButton( - isActive = channelScreenModel.canPost(), - modifier = EditFieldTrailingIconModifier, - ) { - accountViewModel.launchSigner { - channelScreenModel.sendPostSync() - onSendNewMessage() - } + ThinPaddingTextField( + value = channelScreenModel.message, + onValueChange = { channelScreenModel.updateMessage(it) }, + keyboardOptions = PostKeyboard, + shape = EditFieldBorder, + modifier = Modifier.fillMaxWidth(), + placeholder = { + Text( + text = stringRes(R.string.reply_here), + color = MaterialTheme.colorScheme.placeholderText, + ) + }, + trailingIcon = { + ThinSendButton( + isActive = channelScreenModel.canPost(), + modifier = EditFieldTrailingIconModifier, + ) { + accountViewModel.launchSigner { + channelScreenModel.sendPostSync() + onSendNewMessage() } - }, - leadingIcon = { - KeyboardLeadingIcon(channelScreenModel, accountViewModel) - }, - colors = - TextFieldDefaults.colors( - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - ), - visualTransformation = UrlUserTagTransformation(MaterialTheme.colorScheme.primary), + } + }, + leadingIcon = { + KeyboardLeadingIcon(channelScreenModel, accountViewModel) + }, + colors = + TextFieldDefaults.colors( + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + ), + visualTransformation = UrlUserTagTransformation(MaterialTheme.colorScheme.primary), + ) +} + +@Preview +@Composable +fun RecipientMissingRelaysWarningPreview() { + val user1 = LocalCache.getOrCreateUser("460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c") + val user2 = LocalCache.getOrCreateUser("ca89cb11f1c75d5b6622268ff43d2288ea8b2cb5b9aa996ff9ff704fc904b78b") + + ThemeComparisonColumn { + RecipientMissingRelaysWarning( + persistentListOf(user1, user2), + mockAccountViewModel(), + EmptyNav(), ) } } @Composable -fun RecipientMissingRelaysWarning() { +fun RecipientMissingRelaysWarning( + users: ImmutableList, + accountViewModel: AccountViewModel, + nav: INav, +) { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, + horizontalArrangement = SpacedBy10dp, ) { + UserGallery(users) { user -> + ClickableUserPicture( + user, + Size25dp, + accountViewModel, + onClick = { + nav.nav { routeFor(user) } + }, + ) + } + Text( text = stringRes(R.string.recipient_missing_dm_relays), color = MaterialTheme.colorScheme.error, @@ -233,6 +283,40 @@ fun RecipientMissingRelaysWarning() { } } +@Composable +fun UserGallery( + users: ImmutableList, + galleryUser: @Composable RowScope.(user: User) -> Unit, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy((-10).dp), + ) { + users.take(6).forEach { + key(it.pubkeyHex) { + galleryUser(it) + } + } + + if (users.size > 6) { + Box( + contentAlignment = Alignment.Center, + modifier = + Modifier + .size(Size25dp) + .clip(shape = CircleShape) + .background(MaterialTheme.colorScheme.secondaryContainer), + ) { + Text( + text = "+" + showCount(users.size - 6), + fontSize = 10.sp, + color = MaterialTheme.colorScheme.onSurface, + ) + } + } + } +} + @Composable fun KeyboardLeadingIcon( channelScreenModel: ChatNewMessageViewModel, @@ -240,7 +324,7 @@ fun KeyboardLeadingIcon( ) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(start = 4.dp, end = 10.dp), + modifier = Modifier.padding(start = 4.dp, end = 4.dp), ) { SelectFromGallery( isUploading = channelScreenModel.isUploadingImage, @@ -248,8 +332,6 @@ fun KeyboardLeadingIcon( modifier = Modifier, onImageChosen = channelScreenModel::pickedMedia, ) - - Nip17Indicator(channelScreenModel) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileSender.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileSender.kt index 17b4c5831..4337bffa8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileSender.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileSender.kt @@ -25,7 +25,6 @@ import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.quartz.nip01Core.tags.references.references -import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent @@ -100,40 +99,6 @@ class ChatFileSender( account.sendNip17PrivateMessage(template) } - // ------ - // NIP 04 - // ------ - - suspend fun sendNIP04(uploads: List) { - uploads.forEach { - if (it.cipher == null) { - sendNIP04(it.result, it.caption, it.contentWarningReason) - } - } - } - - suspend fun sendNIP04( - result: UploadOrchestrator.OrchestratorResult.ServerResult, - caption: String?, - contentWarningReason: String?, - ) { - val iMetaAttachments = IMetaAttachments() - iMetaAttachments.add(result, caption, contentWarningReason) - - val toUser = chatroom.users.first().let { LocalCache.getOrCreateUser(it).toPTag() } - - val template = - PrivateDmEvent.build( - toUser = toUser, - message = result.url, - imetas = iMetaAttachments.iMetaAttachments, - replyingTo = null, - signer = account.signer, - ) - - account.sendNip04PrivateMessage(template) - } - suspend fun sendAll(uploads: List) { sendNIP17(uploads) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt index d5c06cfcf..e1570b75a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -46,7 +47,8 @@ fun RoomChatFileUploadDialog( accountViewModel: AccountViewModel, nav: INav, ) { - val room = channelScreenModel.room ?: return + val roomState = channelScreenModel.room.collectAsStateWithLifecycle() + val room = roomState.value ?: return val context = LocalContext.current ChatFileUploadDialog( @@ -74,6 +76,6 @@ fun RoomChatFileUploadDialog( onCancel, accountViewModel, nav, - isNip17 = channelScreenModel.nip17, + isNip17 = true, ) } diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 3f186a6d5..136d0096d 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1351,7 +1351,7 @@ Good options are:\n - auth.nostr1.com (free)\n - inbox.nostr.wine (paid)\n - relay.0xchat.com (free) Insert between 1–3 relays to serve as your private inbox. DM Inbox relays should accept any message from anyone, but only allow you to download them. Set up now - This user hasn\'t set up DM inbox relays. Messages cannot be delivered until they configure their relay list. + DM inbox relays not found. Messages cannot be delivered until they configure their relay list. Search Relays Set up your Search relays