feat: deprecate NIP-04 DM sending, always use NIP-17
NIP-04 encryption for sending DMs is now deprecated. All new messages are sent using NIP-17 (gift-wrapped sealed messages). Reading NIP-04 messages remains supported for backward compatibility. When a recipient lacks both a DM relay list (kind 10050) and NIP-65 inbox relays, the send button is disabled and a warning is shown explaining that messages cannot be delivered. Changes: - ChatNewMessageState: Remove nip17/requiresNip17 toggles, add recipientsMissingDmRelays state, always send via NIP-17 - ChatNewMessageViewModel: Always use NIP-17, remove NIP-04 send paths, add recipient relay status checking - ToggleNip17Button -> Nip17Indicator: Replace toggle with static NIP-17 indicator (always on) - PrivateMessageEditFieldRow: Show warning when recipients lack DM relay lists, hide message input - ChatFileSender: sendAll() always uses NIP-17 - Desktop ChatPane: Remove NIP-17 toggle, show relay warning - ChatroomView: Reactively check recipient DM relay availability https://claude.ai/code/session_01T7QhUW9cZogk4DxDXbbbJJ
This commit is contained in:
+20
-66
@@ -28,13 +28,11 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.references.references
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findNostrEventUris
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findURLs
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.BaseDMGroupEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.NIP17Group
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
@@ -62,9 +60,6 @@ class ChatNewMessageState(
|
||||
private val _message = MutableStateFlow(TextFieldValue(""))
|
||||
val message: StateFlow<TextFieldValue> = _message.asStateFlow()
|
||||
|
||||
private val _nip17 = MutableStateFlow(false)
|
||||
val nip17: StateFlow<Boolean> = _nip17.asStateFlow()
|
||||
|
||||
private val _replyTo = MutableStateFlow<Note?>(null)
|
||||
val replyTo: StateFlow<Note?> = _replyTo.asStateFlow()
|
||||
|
||||
@@ -74,38 +69,37 @@ class ChatNewMessageState(
|
||||
private val _room = MutableStateFlow<ChatroomKey?>(null)
|
||||
val room: StateFlow<ChatroomKey?> = _room.asStateFlow()
|
||||
|
||||
/** Whether NIP-17 is required (group chat with >1 recipient) */
|
||||
private val _requiresNip17 = MutableStateFlow(false)
|
||||
val requiresNip17: StateFlow<Boolean> = _requiresNip17.asStateFlow()
|
||||
/** Whether any recipients are missing DM relay lists, preventing message delivery */
|
||||
private val _recipientsMissingDmRelays = MutableStateFlow(false)
|
||||
val recipientsMissingDmRelays: StateFlow<Boolean> = _recipientsMissingDmRelays.asStateFlow()
|
||||
|
||||
/** Whether a message can be sent (non-blank text + room set) */
|
||||
/** Whether a message can be sent (non-blank text + room set + all recipients have DM relays) */
|
||||
val canSend: Boolean
|
||||
get() = _message.value.text.isNotBlank() && _room.value != null
|
||||
get() = _message.value.text.isNotBlank() && _room.value != null && !_recipientsMissingDmRelays.value
|
||||
|
||||
/**
|
||||
* Load a chatroom. Sets the room key, formats toUsers display,
|
||||
* and auto-detects NIP-17 requirement (group chats require NIP-17).
|
||||
* Load a chatroom. Sets the room key and checks recipient DM relay availability.
|
||||
*/
|
||||
fun load(roomKey: ChatroomKey) {
|
||||
_room.value = roomKey
|
||||
updateNip17FromRoom()
|
||||
updateRecipientRelayStatus()
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detect NIP-17 based on room:
|
||||
* - Group chats (>1 recipient) always require NIP-17
|
||||
* - Single recipient: NIP-17 off by default (can be toggled)
|
||||
* Check if all recipients have DM relay lists.
|
||||
* Messages can only be sent via NIP-17, so recipients must have
|
||||
* either a DM inbox relay list (kind 10050) or NIP-65 inbox relays.
|
||||
*/
|
||||
fun updateNip17FromRoom() {
|
||||
fun updateRecipientRelayStatus() {
|
||||
val currentRoom = _room.value
|
||||
if (currentRoom != null) {
|
||||
_requiresNip17.value = currentRoom.users.size > 1
|
||||
if (_requiresNip17.value) {
|
||||
_nip17.value = true
|
||||
}
|
||||
_recipientsMissingDmRelays.value =
|
||||
currentRoom.users.any { hexKey ->
|
||||
val user = cache.getOrCreateUser(hexKey) as? User
|
||||
user?.dmInboxRelays().isNullOrEmpty()
|
||||
}
|
||||
} else {
|
||||
_requiresNip17.value = false
|
||||
_nip17.value = false
|
||||
_recipientsMissingDmRelays.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,27 +120,7 @@ class ChatNewMessageState(
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle NIP-04/NIP-17 mode.
|
||||
* If NIP-17 is required (group chat), stays on NIP-17.
|
||||
*/
|
||||
fun toggleNip17() {
|
||||
if (_requiresNip17.value) {
|
||||
_nip17.value = true
|
||||
} else {
|
||||
_nip17.value = !_nip17.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable NIP-17 (e.g., when recipient has DM relay list).
|
||||
*/
|
||||
fun enableNip17() {
|
||||
_nip17.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the current message. Builds the appropriate event template
|
||||
* (NIP-04 or NIP-17) and delegates to IAccount for signing/broadcasting.
|
||||
* Send the current message as NIP-17. NIP-04 is deprecated for sending.
|
||||
*
|
||||
* @return true if send was initiated, false if preconditions not met
|
||||
*/
|
||||
@@ -154,12 +128,9 @@ class ChatNewMessageState(
|
||||
val currentRoom = _room.value ?: return false
|
||||
val messageText = _message.value.text
|
||||
if (messageText.isBlank()) return false
|
||||
if (_recipientsMissingDmRelays.value) return false
|
||||
|
||||
if (_nip17.value || currentRoom.users.size > 1 || _replyTo.value?.event is NIP17Group) {
|
||||
sendNip17(currentRoom, messageText)
|
||||
} else {
|
||||
sendNip04(currentRoom, messageText)
|
||||
}
|
||||
sendNip17(currentRoom, messageText)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -193,23 +164,6 @@ class ChatNewMessageState(
|
||||
account.sendNip17PrivateMessage(template)
|
||||
}
|
||||
|
||||
private suspend fun sendNip04(
|
||||
room: ChatroomKey,
|
||||
messageText: String,
|
||||
) {
|
||||
val toUser = (cache.getOrCreateUser(room.users.first()) as? User)?.toPTag() ?: return
|
||||
|
||||
val template =
|
||||
PrivateDmEvent.build(
|
||||
toUser = toUser,
|
||||
message = messageText,
|
||||
replyingTo = _replyTo.value?.toEventHint<PrivateDmEvent>(),
|
||||
signer = account.signer,
|
||||
)
|
||||
|
||||
account.sendNip04PrivateMessage(template)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all composition state after sending or cancelling.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user