Adds a check to see if the receiver has NIP-17 relays setup and if so activates NIP-17 message.

pull/940/head
Vitor Pamplona 2024-06-21 15:05:41 -04:00
rodzic fdd1bb9810
commit f8a77d634c
1 zmienionych plików z 102 dodań i 44 usunięć

Wyświetl plik

@ -53,6 +53,7 @@ import com.vitorpamplona.quartz.encoders.Hex
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toNpub import com.vitorpamplona.quartz.encoders.toNpub
import com.vitorpamplona.quartz.events.AddressableEvent import com.vitorpamplona.quartz.events.AddressableEvent
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.events.BaseTextNoteEvent import com.vitorpamplona.quartz.events.BaseTextNoteEvent
import com.vitorpamplona.quartz.events.ChatMessageEvent import com.vitorpamplona.quartz.events.ChatMessageEvent
import com.vitorpamplona.quartz.events.ClassifiedsEvent import com.vitorpamplona.quartz.events.ClassifiedsEvent
@ -88,7 +89,7 @@ enum class UserSuggestionAnchor {
} }
@Stable @Stable
open class NewPostViewModel() : ViewModel() { open class NewPostViewModel : ViewModel() {
var draftTag: String by mutableStateOf(UUID.randomUUID().toString()) var draftTag: String by mutableStateOf(UUID.randomUUID().toString())
var accountViewModel: AccountViewModel? = null var accountViewModel: AccountViewModel? = null
@ -175,17 +176,11 @@ open class NewPostViewModel() : ViewModel() {
val draftTextChanges = Channel<String>(Channel.CONFLATED) val draftTextChanges = Channel<String>(Channel.CONFLATED)
fun lnAddress(): String? { fun lnAddress(): String? = account?.userProfile()?.info?.lnAddress()
return account?.userProfile()?.info?.lnAddress()
}
fun hasLnAddress(): Boolean { fun hasLnAddress(): Boolean = account?.userProfile()?.info?.lnAddress() != null
return account?.userProfile()?.info?.lnAddress() != null
}
fun user(): User? { fun user(): User? = account?.userProfile()
return account?.userProfile()
}
open fun load( open fun load(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
@ -368,15 +363,21 @@ open class NewPostViewModel() : ViewModel() {
} }
originalNote = originalNote =
draftEvent.tags().filter { it.size > 1 && (it[0] == "e" || it[0] == "a") && it.getOrNull(3) == "reply" }.map { draftEvent
LocalCache.checkGetOrCreateNote(it[1]) .tags()
}.firstOrNull() .filter { it.size > 1 && (it[0] == "e" || it[0] == "a") && it.getOrNull(3) == "reply" }
.map {
LocalCache.checkGetOrCreateNote(it[1])
}.firstOrNull()
if (originalNote == null) { if (originalNote == null) {
originalNote = originalNote =
draftEvent.tags().filter { it.size > 1 && (it[0] == "e" || it[0] == "a") && it.getOrNull(3) == "root" }.map { draftEvent
LocalCache.checkGetOrCreateNote(it[1]) .tags()
}.firstOrNull() .filter { it.size > 1 && (it[0] == "e" || it[0] == "a") && it.getOrNull(3) == "root" }
.map {
LocalCache.checkGetOrCreateNote(it[1])
}.firstOrNull()
} }
canUsePoll = originalNote?.event !is PrivateDmEvent && originalNote?.channelHex() == null canUsePoll = originalNote?.event !is PrivateDmEvent && originalNote?.channelHex() == null
@ -403,12 +404,45 @@ open class NewPostViewModel() : ViewModel() {
wantsProduct = draftEvent.kind() == 30402 wantsProduct = draftEvent.kind() == 30402
title = TextFieldValue(draftEvent.tags().filter { it.size > 1 && it[0] == "title" }.map { it[1] }?.firstOrNull() ?: "") title =
price = TextFieldValue(draftEvent.tags().filter { it.size > 1 && it[0] == "price" }.map { it[1] }?.firstOrNull() ?: "") TextFieldValue(
category = TextFieldValue(draftEvent.tags().filter { it.size > 1 && it[0] == "t" }.map { it[1] }?.firstOrNull() ?: "") draftEvent
locationText = TextFieldValue(draftEvent.tags().filter { it.size > 1 && it[0] == "location" }.map { it[1] }?.firstOrNull() ?: "") .tags()
.filter { it.size > 1 && it[0] == "title" }
.map { it[1] }
?.firstOrNull() ?: "",
)
price =
TextFieldValue(
draftEvent
.tags()
.filter { it.size > 1 && it[0] == "price" }
.map { it[1] }
?.firstOrNull() ?: "",
)
category =
TextFieldValue(
draftEvent
.tags()
.filter { it.size > 1 && it[0] == "t" }
.map { it[1] }
?.firstOrNull() ?: "",
)
locationText =
TextFieldValue(
draftEvent
.tags()
.filter { it.size > 1 && it[0] == "location" }
.map { it[1] }
?.firstOrNull() ?: "",
)
condition = ClassifiedsEvent.CONDITION.entries.firstOrNull { condition = ClassifiedsEvent.CONDITION.entries.firstOrNull {
it.value == draftEvent.tags().filter { it.size > 1 && it[0] == "condition" }.map { it[1] }.firstOrNull() it.value ==
draftEvent
.tags()
.filter { it.size > 1 && it[0] == "condition" }
.map { it[1] }
.firstOrNull()
} ?: ClassifiedsEvent.CONDITION.USED_LIKE_NEW } ?: ClassifiedsEvent.CONDITION.USED_LIKE_NEW
wantsDirectMessage = draftEvent is PrivateDmEvent || draftEvent is ChatMessageEvent wantsDirectMessage = draftEvent is PrivateDmEvent || draftEvent is ChatMessageEvent
@ -479,7 +513,8 @@ open class NewPostViewModel() : ViewModel() {
if (split.percentage > 0.00001) { if (split.percentage > 0.00001) {
val homeRelay = val homeRelay =
accountViewModel?.getRelayListFor(split.key)?.writeRelays()?.firstOrNull() accountViewModel?.getRelayListFor(split.key)?.writeRelays()?.firstOrNull()
?: split.key.relaysBeingUsed.keys.firstOrNull { !it.contains("localhost") } ?: split.key.relaysBeingUsed.keys
.firstOrNull { !it.contains("localhost") }
ZapSplitSetup( ZapSplitSetup(
lnAddressOrPubKeyHex = split.key.pubkeyHex, lnAddressOrPubKeyHex = split.key.pubkeyHex,
@ -851,9 +886,7 @@ open class NewPostViewModel() : ViewModel() {
} }
} }
open fun findUrlInMessage(): String? { open fun findUrlInMessage(): String? = RichTextParser().parseValidUrls(message.text).firstOrNull()
return RichTextParser().parseValidUrls(message.text).firstOrNull()
}
open fun removeFromReplyList(userToRemove: User) { open fun removeFromReplyList(userToRemove: User) {
pTags = pTags?.filter { it != userToRemove } pTags = pTags?.filter { it != userToRemove }
@ -869,14 +902,18 @@ open class NewPostViewModel() : ViewModel() {
if (it.selection.collapsed) { if (it.selection.collapsed) {
val lastWord = val lastWord =
it.text.substring(0, it.selection.end).substringAfterLast("\n").substringAfterLast(" ") it.text
.substring(0, it.selection.end)
.substringAfterLast("\n")
.substringAfterLast(" ")
userSuggestionAnchor = it.selection userSuggestionAnchor = it.selection
userSuggestionsMainMessage = UserSuggestionAnchor.MAIN_MESSAGE userSuggestionsMainMessage = UserSuggestionAnchor.MAIN_MESSAGE
if (lastWord.startsWith("@") && lastWord.length > 2) { if (lastWord.startsWith("@") && lastWord.length > 2) {
NostrSearchEventOrUserDataSource.search(lastWord.removePrefix("@")) NostrSearchEventOrUserDataSource.search(lastWord.removePrefix("@"))
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
userSuggestions = userSuggestions =
LocalCache.findUsersStartingWith(lastWord.removePrefix("@")) LocalCache
.findUsersStartingWith(lastWord.removePrefix("@"))
.sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex })) .sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex }))
.reversed() .reversed()
} }
@ -894,14 +931,18 @@ open class NewPostViewModel() : ViewModel() {
if (it.selection.collapsed) { if (it.selection.collapsed) {
val lastWord = val lastWord =
it.text.substring(0, it.selection.end).substringAfterLast("\n").substringAfterLast(" ") it.text
.substring(0, it.selection.end)
.substringAfterLast("\n")
.substringAfterLast(" ")
userSuggestionAnchor = it.selection userSuggestionAnchor = it.selection
userSuggestionsMainMessage = UserSuggestionAnchor.TO_USERS userSuggestionsMainMessage = UserSuggestionAnchor.TO_USERS
if (lastWord.startsWith("@") && lastWord.length > 2) { if (lastWord.startsWith("@") && lastWord.length > 2) {
NostrSearchEventOrUserDataSource.search(lastWord.removePrefix("@")) NostrSearchEventOrUserDataSource.search(lastWord.removePrefix("@"))
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
userSuggestions = userSuggestions =
LocalCache.findUsersStartingWith(lastWord.removePrefix("@")) LocalCache
.findUsersStartingWith(lastWord.removePrefix("@"))
.sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex })) .sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex }))
.reversed() .reversed()
} }
@ -928,15 +969,15 @@ open class NewPostViewModel() : ViewModel() {
NostrSearchEventOrUserDataSource.search(lastWord.removePrefix("@")) NostrSearchEventOrUserDataSource.search(lastWord.removePrefix("@"))
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
userSuggestions = userSuggestions =
LocalCache.findUsersStartingWith(lastWord.removePrefix("@")) LocalCache
.findUsersStartingWith(lastWord.removePrefix("@"))
.sortedWith( .sortedWith(
compareBy( compareBy(
{ account?.isFollowing(it) }, { account?.isFollowing(it) },
{ it.toBestDisplayName() }, { it.toBestDisplayName() },
{ it.pubkeyHex }, { it.pubkeyHex },
), ),
) ).reversed()
.reversed()
} }
} else { } else {
NostrSearchEventOrUserDataSource.clear() NostrSearchEventOrUserDataSource.clear()
@ -949,7 +990,10 @@ open class NewPostViewModel() : ViewModel() {
userSuggestionAnchor?.let { userSuggestionAnchor?.let {
if (userSuggestionsMainMessage == UserSuggestionAnchor.MAIN_MESSAGE) { if (userSuggestionsMainMessage == UserSuggestionAnchor.MAIN_MESSAGE) {
val lastWord = val lastWord =
message.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ") message.text
.substring(0, it.end)
.substringAfterLast("\n")
.substringAfterLast(" ")
val lastWordStart = it.end - lastWord.length val lastWordStart = it.end - lastWord.length
val wordToInsert = "@${item.pubkeyNpub()}" val wordToInsert = "@${item.pubkeyNpub()}"
@ -963,7 +1007,10 @@ open class NewPostViewModel() : ViewModel() {
forwardZapToEditting = TextFieldValue("") forwardZapToEditting = TextFieldValue("")
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.TO_USERS) { } else if (userSuggestionsMainMessage == UserSuggestionAnchor.TO_USERS) {
val lastWord = val lastWord =
toUsers.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ") toUsers.text
.substring(0, it.end)
.substringAfterLast("\n")
.substringAfterLast(" ")
val lastWordStart = it.end - lastWord.length val lastWordStart = it.end - lastWord.length
val wordToInsert = "@${item.pubkeyNpub()}" val wordToInsert = "@${item.pubkeyNpub()}"
@ -972,6 +1019,9 @@ open class NewPostViewModel() : ViewModel() {
toUsers.text.replaceRange(lastWordStart, it.end, wordToInsert), toUsers.text.replaceRange(lastWordStart, it.end, wordToInsert),
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length), TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length),
) )
val relayList = (LocalCache.getAddressableNoteIfExists(AdvertisedRelayListEvent.createAddressTag(item.pubkeyHex))?.event as? AdvertisedRelayListEvent)?.readRelays()
nip17 = relayList != null
} }
userSuggestionAnchor = null userSuggestionAnchor = null
@ -982,12 +1032,10 @@ open class NewPostViewModel() : ViewModel() {
saveDraft() saveDraft()
} }
private fun newStateMapPollOptions(): SnapshotStateMap<Int, String> { private fun newStateMapPollOptions(): SnapshotStateMap<Int, String> = mutableStateMapOf(Pair(0, ""), Pair(1, ""))
return mutableStateMapOf(Pair(0, ""), Pair(1, ""))
}
fun canPost(): Boolean { fun canPost(): Boolean =
return message.text.isNotBlank() && message.text.isNotBlank() &&
!isUploadingImage && !isUploadingImage &&
!wantsInvoice && !wantsInvoice &&
(!wantsZapraiser || zapRaiserAmount != null) && (!wantsZapraiser || zapRaiserAmount != null) &&
@ -1009,7 +1057,6 @@ open class NewPostViewModel() : ViewModel() {
) )
) && ) &&
contentToAddUrl == null contentToAddUrl == null
}
suspend fun createNIP94Record( suspend fun createNIP94Record(
uploadingResult: Nip96Uploader.PartialEvent, uploadingResult: Nip96Uploader.PartialEvent,
@ -1020,11 +1067,20 @@ open class NewPostViewModel() : ViewModel() {
// Images don't seem to be ready immediately after upload // Images don't seem to be ready immediately after upload
val imageUrl = uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "url" }?.get(1) val imageUrl = uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "url" }?.get(1)
val remoteMimeType = val remoteMimeType =
uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "m" }?.get(1)?.ifBlank { null } uploadingResult.tags
?.firstOrNull { it.size > 1 && it[0] == "m" }
?.get(1)
?.ifBlank { null }
val originalHash = val originalHash =
uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "ox" }?.get(1)?.ifBlank { null } uploadingResult.tags
?.firstOrNull { it.size > 1 && it[0] == "ox" }
?.get(1)
?.ifBlank { null }
val dim = val dim =
uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "dim" }?.get(1)?.ifBlank { null } uploadingResult.tags
?.firstOrNull { it.size > 1 && it[0] == "dim" }
?.get(1)
?.ifBlank { null }
val magnet = val magnet =
uploadingResult.tags uploadingResult.tags
?.firstOrNull { it.size > 1 && it[0] == "magnet" } ?.firstOrNull { it.size > 1 && it[0] == "magnet" }
@ -1270,7 +1326,9 @@ open class NewPostViewModel() : ViewModel() {
} }
} }
enum class GeohashPrecision(val digits: Int) { enum class GeohashPrecision(
val digits: Int,
) {
KM_5000_X_5000(1), // 5,000km × 5,000km KM_5000_X_5000(1), // 5,000km × 5,000km
KM_1250_X_625(2), // 1,250km × 625km KM_1250_X_625(2), // 1,250km × 625km
KM_156_X_156(3), // 156km × 156km KM_156_X_156(3), // 156km × 156km