mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-28 22:53:11 +02:00
Format last files with conflicts
This commit is contained in:
@@ -14,27 +14,21 @@ import com.vitorpamplona.amethyst.service.model.ChannelHideMessageEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ChannelMuteUserEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ReactionEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
||||
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ContactListEvent
|
||||
import com.vitorpamplona.amethyst.service.model.DeletionEvent
|
||||
import com.vitorpamplona.amethyst.service.model.Event
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
|
||||
import com.vitorpamplona.amethyst.service.model.MetadataEvent
|
||||
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ReactionEvent
|
||||
import com.vitorpamplona.amethyst.service.model.RecommendRelayEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
||||
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
||||
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
|
||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||
import fr.acinq.secp256k1.Hex
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -43,7 +37,12 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import nostr.postr.toNpub
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
object LocalCache {
|
||||
val metadataParser = jacksonObjectMapper()
|
||||
@@ -108,7 +107,6 @@ object LocalCache {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Synchronized
|
||||
fun getOrCreateChannel(key: String): Channel {
|
||||
return channels[key] ?: run {
|
||||
@@ -121,10 +119,11 @@ object LocalCache {
|
||||
fun checkGetOrCreateAddressableNote(key: String): AddressableNote? {
|
||||
return try {
|
||||
val addr = ATag.parse(key, null) // relay doesn't matter for the index.
|
||||
if (addr != null)
|
||||
if (addr != null) {
|
||||
getOrCreateAddressableNote(addr)
|
||||
else
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e("LocalCache", "Invalid Key to create channel: $key", e)
|
||||
null
|
||||
@@ -159,9 +158,9 @@ object LocalCache {
|
||||
}
|
||||
|
||||
oldUser.updateUserInfo(newUser, event)
|
||||
//Log.d("MT", "New User Metadata ${oldUser.pubkeyDisplayHex} ${oldUser.toBestDisplayName()}")
|
||||
// Log.d("MT", "New User Metadata ${oldUser.pubkeyDisplayHex} ${oldUser.toBestDisplayName()}")
|
||||
} else {
|
||||
//Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()} ${formattedDateTime(event.createdAt)} > ${formattedDateTime(oldUser.updatedAt)}")
|
||||
// Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()} ${formattedDateTime(event.createdAt)} > ${formattedDateTime(oldUser.updatedAt)}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +169,6 @@ object LocalCache {
|
||||
.format(DateTimeFormatter.ofPattern("uuuu MMM d hh:mm a"))
|
||||
}
|
||||
|
||||
|
||||
fun consume(event: TextNoteEvent, relay: Relay? = null) {
|
||||
val note = getOrCreateNote(event.id)
|
||||
val author = getOrCreateUser(event.pubKey)
|
||||
@@ -196,7 +194,7 @@ object LocalCache {
|
||||
|
||||
note.loadEvent(event, author, mentions, replyTo)
|
||||
|
||||
//Log.d("TN", "New Note (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${note.event?.content()?.take(100)} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("TN", "New Note (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${note.event?.content()?.take(100)} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
// Prepares user's profile view.
|
||||
author.addNote(note)
|
||||
@@ -295,7 +293,7 @@ object LocalCache {
|
||||
// Already processed this event.
|
||||
if (note.event != null) return
|
||||
|
||||
//Log.d("TN", "New Boost (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("TN", "New Boost (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
val author = getOrCreateUser(event.pubKey)
|
||||
val awardees = event.awardees().mapNotNull { checkGetOrCreateUser(it) }
|
||||
@@ -332,7 +330,6 @@ object LocalCache {
|
||||
citations.add(tag[1])
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
return citations
|
||||
@@ -365,7 +362,7 @@ object LocalCache {
|
||||
}
|
||||
|
||||
fun consume(event: RecommendRelayEvent) {
|
||||
//Log.d("RR", event.toJson())
|
||||
// Log.d("RR", event.toJson())
|
||||
}
|
||||
|
||||
fun consume(event: ContactListEvent) {
|
||||
@@ -383,7 +380,7 @@ object LocalCache {
|
||||
getOrCreateUser(pubKey.toHexKey())
|
||||
} catch (e: Exception) {
|
||||
Log.w("ContactList Parser", "Ignoring: Could not parse Hex key: ${it.pubKeyHex} in ${event.toJson()}")
|
||||
//e.printStackTrace()
|
||||
// e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}.filterNotNull().toSet(),
|
||||
@@ -402,7 +399,7 @@ object LocalCache {
|
||||
user.updateRelays(relays)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w("Relay List Parser","Relay import issue ${e.message}", e)
|
||||
Log.w("Relay List Parser", "Relay import issue ${e.message}", e)
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
@@ -424,7 +421,7 @@ object LocalCache {
|
||||
|
||||
val recipient = event.recipientPubKey()?.let { getOrCreateUser(it) }
|
||||
|
||||
//Log.d("PM", "${author.toBestDisplayName()} to ${recipient?.toBestDisplayName()}")
|
||||
// Log.d("PM", "${author.toBestDisplayName()} to ${recipient?.toBestDisplayName()}")
|
||||
|
||||
val repliesTo = event.tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }.mapNotNull { checkGetOrCreateNote(it) }
|
||||
val mentions = event.tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) }.mapNotNull { checkGetOrCreateUser(it) }
|
||||
@@ -483,7 +480,7 @@ object LocalCache {
|
||||
// Already processed this event.
|
||||
if (note.event != null) return
|
||||
|
||||
//Log.d("TN", "New Boost (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("TN", "New Boost (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
val author = getOrCreateUser(event.pubKey)
|
||||
val mentions = event.originalAuthor().mapNotNull { checkGetOrCreateUser(it) }
|
||||
@@ -524,7 +521,7 @@ object LocalCache {
|
||||
|
||||
note.loadEvent(event, author, mentions, repliesTo)
|
||||
|
||||
//Log.d("RE", "New Reaction ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("RE", "New Reaction ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
// Adds notifications to users.
|
||||
mentions.forEach {
|
||||
@@ -547,8 +544,8 @@ object LocalCache {
|
||||
}
|
||||
}
|
||||
|
||||
if (event.content == "!" // nostr_console hide.
|
||||
|| event.content == "\u26A0\uFE0F" // Warning sign
|
||||
if (event.content == "!" || // nostr_console hide.
|
||||
event.content == "\u26A0\uFE0F" // Warning sign
|
||||
) {
|
||||
// Counts the replies
|
||||
repliesTo.forEach {
|
||||
@@ -575,7 +572,7 @@ object LocalCache {
|
||||
|
||||
note.loadEvent(event, author, mentions, repliesTo)
|
||||
|
||||
//Log.d("RP", "New Report ${event.content} by ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("RP", "New Report ${event.content} by ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
// Adds notifications to users.
|
||||
if (repliesTo.isEmpty()) {
|
||||
mentions.forEach {
|
||||
@@ -588,7 +585,7 @@ object LocalCache {
|
||||
}
|
||||
|
||||
fun consume(event: ChannelCreateEvent) {
|
||||
//Log.d("MT", "New Event ${event.content} ${event.id.toHex()}")
|
||||
// Log.d("MT", "New Event ${event.content} ${event.id.toHex()}")
|
||||
// new event
|
||||
val oldChannel = getOrCreateChannel(event.id)
|
||||
val author = getOrCreateUser(event.pubKey)
|
||||
@@ -609,7 +606,7 @@ object LocalCache {
|
||||
|
||||
fun consume(event: ChannelMetadataEvent) {
|
||||
val channelId = event.channel()
|
||||
//Log.d("MT", "New User ${users.size} ${event.contactMetaData.name}")
|
||||
// Log.d("MT", "New User ${users.size} ${event.contactMetaData.name}")
|
||||
if (channelId.isNullOrBlank()) return
|
||||
|
||||
// new event
|
||||
@@ -626,7 +623,7 @@ object LocalCache {
|
||||
refreshObservers()
|
||||
}
|
||||
} else {
|
||||
//Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()} ${formattedDateTime(event.createdAt)} > ${formattedDateTime(oldUser.updatedAt)}")
|
||||
// Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()} ${formattedDateTime(event.createdAt)} > ${formattedDateTime(oldUser.updatedAt)}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,7 +661,7 @@ object LocalCache {
|
||||
|
||||
note.loadEvent(event, author, mentions, replyTo)
|
||||
|
||||
//Log.d("CM", "New Note (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${note.event?.content()} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("CM", "New Note (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${note.event?.content()} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
// Adds notifications to users.
|
||||
mentions.forEach {
|
||||
@@ -683,11 +680,9 @@ object LocalCache {
|
||||
}
|
||||
|
||||
fun consume(event: ChannelHideMessageEvent) {
|
||||
|
||||
}
|
||||
|
||||
fun consume(event: ChannelMuteUserEvent) {
|
||||
|
||||
}
|
||||
|
||||
fun consume(event: LnZapEvent) {
|
||||
@@ -707,11 +702,11 @@ object LocalCache {
|
||||
note.loadEvent(event, author, mentions, repliesTo)
|
||||
|
||||
if (zapRequest == null) {
|
||||
Log.e("ZP","Zap Request not found. Unable to process Zap {${event.toJson()}}")
|
||||
Log.e("ZP", "Zap Request not found. Unable to process Zap {${event.toJson()}}")
|
||||
return
|
||||
}
|
||||
|
||||
//Log.d("ZP", "New ZapEvent ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("ZP", "New ZapEvent ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
// Adds notifications to users.
|
||||
mentions.forEach {
|
||||
@@ -742,7 +737,7 @@ object LocalCache {
|
||||
|
||||
note.loadEvent(event, author, mentions, repliesTo)
|
||||
|
||||
//Log.d("ZP", "New Zap Request ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
// Log.d("ZP", "New Zap Request ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
|
||||
|
||||
// Adds notifications to users.
|
||||
mentions.forEach {
|
||||
@@ -762,31 +757,31 @@ object LocalCache {
|
||||
|
||||
fun findUsersStartingWith(username: String): List<User> {
|
||||
return users.values.filter {
|
||||
(it.anyNameStartsWith(username))
|
||||
|| it.pubkeyHex.startsWith(username, true)
|
||||
|| it.pubkeyNpub().startsWith(username, true)
|
||||
(it.anyNameStartsWith(username)) ||
|
||||
it.pubkeyHex.startsWith(username, true) ||
|
||||
it.pubkeyNpub().startsWith(username, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun findNotesStartingWith(text: String): List<Note> {
|
||||
return notes.values.filter {
|
||||
(it.event is TextNoteEvent && it.event?.content()?.contains(text, true) ?: false)
|
||||
|| (it.event is ChannelMessageEvent && it.event?.content()?.contains(text, true) ?: false)
|
||||
|| it.idHex.startsWith(text, true)
|
||||
|| it.idNote().startsWith(text, true)
|
||||
(it.event is TextNoteEvent && it.event?.content()?.contains(text, true) ?: false) ||
|
||||
(it.event is ChannelMessageEvent && it.event?.content()?.contains(text, true) ?: false) ||
|
||||
it.idHex.startsWith(text, true) ||
|
||||
it.idNote().startsWith(text, true)
|
||||
} + addressables.values.filter {
|
||||
(it.event as? LongTextNoteEvent)?.content?.contains(text, true) ?: false
|
||||
|| (it.event as? LongTextNoteEvent)?.title()?.contains(text, true) ?: false
|
||||
|| (it.event as? LongTextNoteEvent)?.summary()?.contains(text, true) ?: false
|
||||
|| it.idHex.startsWith(text, true)
|
||||
(it.event as? LongTextNoteEvent)?.content?.contains(text, true) ?: false ||
|
||||
(it.event as? LongTextNoteEvent)?.title()?.contains(text, true) ?: false ||
|
||||
(it.event as? LongTextNoteEvent)?.summary()?.contains(text, true) ?: false ||
|
||||
it.idHex.startsWith(text, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun findChannelsStartingWith(text: String): List<Channel> {
|
||||
return channels.values.filter {
|
||||
it.anyNameStartsWith(text)
|
||||
|| it.idHex.startsWith(text, true)
|
||||
|| it.idNote().startsWith(text, true)
|
||||
it.anyNameStartsWith(text) ||
|
||||
it.idHex.startsWith(text, true) ||
|
||||
it.idNote().startsWith(text, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -903,7 +898,7 @@ object LocalCache {
|
||||
}
|
||||
}
|
||||
|
||||
class LocalCacheLiveData(val cache: LocalCache): LiveData<LocalCacheState>(LocalCacheState(cache)) {
|
||||
class LocalCacheLiveData(val cache: LocalCache) : LiveData<LocalCacheState>(LocalCacheState(cache)) {
|
||||
|
||||
// Refreshes observers in batches.
|
||||
var handlerWaiting = AtomicBoolean()
|
||||
@@ -930,6 +925,4 @@ class LocalCacheLiveData(val cache: LocalCache): LiveData<LocalCacheState>(Local
|
||||
}
|
||||
}
|
||||
|
||||
class LocalCacheState(val cache: LocalCache) {
|
||||
|
||||
}
|
||||
class LocalCacheState(val cache: LocalCache)
|
||||
|
@@ -6,13 +6,6 @@ import com.vitorpamplona.amethyst.service.model.*
|
||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||
import fr.acinq.secp256k1.Hex
|
||||
import java.math.BigDecimal
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Date
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.regex.Pattern
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -20,11 +13,17 @@ import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Date
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.regex.Pattern
|
||||
|
||||
val tagSearch = Pattern.compile("(?:\\s|\\A)\\#\\[([0-9]+)\\]")
|
||||
|
||||
|
||||
class AddressableNote(val address: ATag): Note(address.toTag()) {
|
||||
class AddressableNote(val address: ATag) : Note(address.toTag()) {
|
||||
override fun idNote() = address.toNAddr()
|
||||
override fun idDisplayNote() = idNote().toShortenHex()
|
||||
override fun address() = address
|
||||
@@ -62,9 +61,9 @@ open class Note(val idHex: String) {
|
||||
|
||||
fun channel(): Channel? {
|
||||
val channelHex =
|
||||
(event as? ChannelMessageEvent)?.channel() ?:
|
||||
(event as? ChannelMetadataEvent)?.channel() ?:
|
||||
(event as? ChannelCreateEvent)?.let { it.id }
|
||||
(event as? ChannelMessageEvent)?.channel()
|
||||
?: (event as? ChannelMetadataEvent)?.channel()
|
||||
?: (event as? ChannelCreateEvent)?.let { it.id }
|
||||
|
||||
return channelHex?.let { LocalCache.checkGetOrCreateChannel(it) }
|
||||
}
|
||||
@@ -137,7 +136,7 @@ open class Note(val idHex: String) {
|
||||
fun removeReport(deleteNote: Note) {
|
||||
val author = deleteNote.author ?: return
|
||||
|
||||
if (author in reports.keys && reports[author]?.contains(deleteNote) == true ) {
|
||||
if (author in reports.keys && reports[author]?.contains(deleteNote) == true) {
|
||||
reports[author]?.let {
|
||||
reports = reports + Pair(author, it.minus(deleteNote))
|
||||
liveSet?.reports?.invalidateData()
|
||||
@@ -156,7 +155,6 @@ open class Note(val idHex: String) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun addBoost(note: Note) {
|
||||
if (note !in boosts) {
|
||||
boosts = boosts + note
|
||||
@@ -240,11 +238,13 @@ open class Note(val idHex: String) {
|
||||
}
|
||||
|
||||
fun hasAnyReports(): Boolean {
|
||||
val dayAgo = Date().time / 1000 - 24*60*60
|
||||
val dayAgo = Date().time / 1000 - 24 * 60 * 60
|
||||
return reports.isNotEmpty() ||
|
||||
(author?.reports?.values?.filter {
|
||||
it.firstOrNull { ( it.createdAt() ?: 0 ) > dayAgo } != null
|
||||
}?.isNotEmpty() ?: false)
|
||||
(
|
||||
author?.reports?.values?.filter {
|
||||
it.firstOrNull { (it.createdAt() ?: 0) > dayAgo } != null
|
||||
}?.isNotEmpty() ?: false
|
||||
)
|
||||
}
|
||||
|
||||
fun directlyCiteUsersHex(): Set<HexKey> {
|
||||
@@ -257,7 +257,6 @@ open class Note(val idHex: String) {
|
||||
returningList.add(tag[1])
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
return returningList
|
||||
@@ -275,17 +274,16 @@ open class Note(val idHex: String) {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
return returningList
|
||||
}
|
||||
|
||||
fun directlyCites(userProfile: User): Boolean {
|
||||
return author == userProfile
|
||||
|| (userProfile in directlyCiteUsers())
|
||||
|| (event is ReactionEvent && replyTo?.lastOrNull()?.directlyCites(userProfile) == true)
|
||||
|| (event is RepostEvent && replyTo?.lastOrNull()?.directlyCites(userProfile) == true)
|
||||
return author == userProfile ||
|
||||
(userProfile in directlyCiteUsers()) ||
|
||||
(event is ReactionEvent && replyTo?.lastOrNull()?.directlyCites(userProfile) == true) ||
|
||||
(event is RepostEvent && replyTo?.lastOrNull()?.directlyCites(userProfile) == true)
|
||||
}
|
||||
|
||||
fun isNewThread(): Boolean {
|
||||
@@ -306,7 +304,7 @@ open class Note(val idHex: String) {
|
||||
|
||||
fun hasBoostedInTheLast5Minutes(loggedIn: User): Boolean {
|
||||
val currentTime = Date().time / 1000
|
||||
return boosts.firstOrNull { it.author == loggedIn && (it.createdAt() ?: 0) > currentTime - (60 * 5)} != null // 5 minute protection
|
||||
return boosts.firstOrNull { it.author == loggedIn && (it.createdAt() ?: 0) > currentTime - (60 * 5) } != null // 5 minute protection
|
||||
}
|
||||
|
||||
fun boostedBy(loggedIn: User): List<Note> {
|
||||
@@ -329,7 +327,6 @@ open class Note(val idHex: String) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class NoteLiveSet(u: Note) {
|
||||
// Observers line up here.
|
||||
val metadata: NoteLiveData = NoteLiveData(u)
|
||||
@@ -342,17 +339,17 @@ class NoteLiveSet(u: Note) {
|
||||
val zaps: NoteLiveData = NoteLiveData(u)
|
||||
|
||||
fun isInUse(): Boolean {
|
||||
return metadata.hasObservers()
|
||||
|| reactions.hasObservers()
|
||||
|| boosts.hasObservers()
|
||||
|| replies.hasObservers()
|
||||
|| reports.hasObservers()
|
||||
|| relays.hasObservers()
|
||||
|| zaps.hasObservers()
|
||||
return metadata.hasObservers() ||
|
||||
reactions.hasObservers() ||
|
||||
boosts.hasObservers() ||
|
||||
replies.hasObservers() ||
|
||||
reports.hasObservers() ||
|
||||
relays.hasObservers() ||
|
||||
zaps.hasObservers()
|
||||
}
|
||||
}
|
||||
|
||||
class NoteLiveData(val note: Note): LiveData<NoteState>(NoteState(note)) {
|
||||
class NoteLiveData(val note: Note) : LiveData<NoteState>(NoteState(note)) {
|
||||
// Refreshes observers in batches.
|
||||
var handlerWaiting = AtomicBoolean()
|
||||
|
||||
@@ -384,7 +381,6 @@ class NoteLiveData(val note: Note): LiveData<NoteState>(NoteState(note)) {
|
||||
} else {
|
||||
NostrSingleEventDataSource.add(note)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
|
@@ -19,10 +19,11 @@ class ThreadAssembler {
|
||||
|
||||
// recursive
|
||||
val roots = note.replyTo?.map {
|
||||
if (it !in testedNotes)
|
||||
if (it !in testedNotes) {
|
||||
searchRoot(it, testedNotes)
|
||||
else
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}?.filterNotNull()
|
||||
|
||||
if (roots != null && roots.isNotEmpty()) {
|
||||
@@ -37,15 +38,15 @@ class ThreadAssembler {
|
||||
val (result, elapsed) = measureTimedValue {
|
||||
val note = if (noteId.contains(":")) {
|
||||
val aTag = ATag.parse(noteId, null)
|
||||
if (aTag != null)
|
||||
if (aTag != null) {
|
||||
LocalCache.getOrCreateAddressableNote(aTag)
|
||||
else
|
||||
} else {
|
||||
return emptySet()
|
||||
}
|
||||
} else {
|
||||
LocalCache.getOrCreateNote(noteId)
|
||||
}
|
||||
|
||||
|
||||
if (note.event != null) {
|
||||
val thread = mutableSetOf<Note>()
|
||||
|
||||
@@ -59,7 +60,7 @@ class ThreadAssembler {
|
||||
}
|
||||
}
|
||||
|
||||
println("Model Refresh: Thread loaded in ${elapsed}")
|
||||
println("Model Refresh: Thread loaded in $elapsed")
|
||||
|
||||
return result
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class Nip19 {
|
||||
return naddr(bytes)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
println("Issue trying to Decode NIP19 ${uri}: ${e.message}")
|
||||
println("Issue trying to Decode NIP19 $uri: ${e.message}")
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -43,7 +43,7 @@ class Nip19 {
|
||||
}
|
||||
|
||||
private fun note(bytes: ByteArray): Return {
|
||||
return Return(Type.NOTE, bytes.toHexKey(), null);
|
||||
return Return(Type.NOTE, bytes.toHexKey(), null)
|
||||
}
|
||||
|
||||
private fun nprofile(bytes: ByteArray): Return? {
|
||||
|
@@ -4,16 +4,16 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
|
||||
import com.vitorpamplona.amethyst.service.model.BadgeProfilesEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ContactListEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
|
||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||
import com.vitorpamplona.amethyst.service.model.ContactListEvent
|
||||
import com.vitorpamplona.amethyst.service.model.MetadataEvent
|
||||
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
|
||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||
|
||||
object NostrUserProfileDataSource: NostrDataSource("UserProfileFeed") {
|
||||
object NostrUserProfileDataSource : NostrDataSource("UserProfileFeed") {
|
||||
var user: User? = null
|
||||
|
||||
fun loadUserProfile(userId: String?) {
|
||||
|
@@ -23,8 +23,9 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela
|
||||
var fullArray =
|
||||
byteArrayOf(NIP19TLVTypes.SPECIAL.id, dTag.size.toByte()) + dTag
|
||||
|
||||
if (relay != null)
|
||||
if (relay != null) {
|
||||
fullArray = fullArray + byteArrayOf(NIP19TLVTypes.RELAY.id, relay.size.toByte()) + relay
|
||||
}
|
||||
|
||||
fullArray = fullArray +
|
||||
byteArrayOf(NIP19TLVTypes.AUTHOR.id, author.size.toByte()) + author +
|
||||
@@ -39,11 +40,12 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela
|
||||
}
|
||||
|
||||
fun parse(address: String, relay: String?): ATag? {
|
||||
return if (address.startsWith("naddr") || address.startsWith("nostr:naddr"))
|
||||
return if (address.startsWith("naddr") || address.startsWith("nostr:naddr")) {
|
||||
parseNAddr(address)
|
||||
else
|
||||
} else {
|
||||
parseAtag(address, relay)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseAtag(atag: String, relay: String?): ATag? {
|
||||
return try {
|
||||
@@ -51,7 +53,7 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela
|
||||
Hex.decode(parts[1])
|
||||
ATag(parts[0].toInt(), parts[1], parts[2], relay)
|
||||
} catch (t: Throwable) {
|
||||
Log.w("ATag", "Error parsing A Tag: ${atag}: ${t.message}")
|
||||
Log.w("ATag", "Error parsing A Tag: $atag: ${t.message}")
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -67,13 +69,13 @@ data class ATag(val kind: Int, val pubKeyHex: String, val dTag: String, val rela
|
||||
val author = tlv.get(NIP19TLVTypes.AUTHOR.id)?.get(0)?.toHexKey()
|
||||
val kind = tlv.get(NIP19TLVTypes.KIND.id)?.get(0)?.let { toInt32(it) }
|
||||
|
||||
if (kind != null && author != null)
|
||||
if (kind != null && author != null) {
|
||||
return ATag(kind, author, d, relay)
|
||||
}
|
||||
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.w( "ATag", "Issue trying to Decode NIP19 ${this}: ${e.message}")
|
||||
//e.printStackTrace()
|
||||
Log.w("ATag", "Issue trying to Decode NIP19 $this: ${e.message}")
|
||||
// e.printStackTrace()
|
||||
}
|
||||
|
||||
return null
|
||||
|
@@ -12,7 +12,7 @@ class LnZapEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
): LnZapEventInterface, Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
) : LnZapEventInterface, Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
override fun zappedPost() = tags
|
||||
.filter { it.firstOrNull() == "e" }
|
||||
|
@@ -2,18 +2,17 @@ package com.vitorpamplona.amethyst.service.model
|
||||
|
||||
import com.vitorpamplona.amethyst.model.HexKey
|
||||
import com.vitorpamplona.amethyst.model.toHexKey
|
||||
import java.util.Date
|
||||
import nostr.postr.Utils
|
||||
import nostr.postr.toHex
|
||||
import java.util.Date
|
||||
|
||||
class LnZapRequestEvent (
|
||||
class LnZapRequestEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
): Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun zappedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }
|
||||
fun zappedAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) }
|
||||
fun taggedAddresses() = tags.filter { it.firstOrNull() == "a" }.mapNotNull {
|
||||
@@ -35,7 +34,7 @@ class LnZapRequestEvent (
|
||||
listOf("relays") + relays
|
||||
)
|
||||
if (originalNote is LongTextNoteEvent) {
|
||||
tags = tags + listOf( listOf("a", originalNote.address().toTag()) )
|
||||
tags = tags + listOf(listOf("a", originalNote.address().toTag()))
|
||||
}
|
||||
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
|
@@ -2,18 +2,17 @@ package com.vitorpamplona.amethyst.service.model
|
||||
|
||||
import com.vitorpamplona.amethyst.model.HexKey
|
||||
import com.vitorpamplona.amethyst.model.toHexKey
|
||||
import java.util.Date
|
||||
import nostr.postr.Utils
|
||||
import nostr.postr.toHex
|
||||
import java.util.Date
|
||||
|
||||
class ReactionEvent (
|
||||
class ReactionEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
): Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
fun originalPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }
|
||||
fun originalAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) }
|
||||
@@ -38,9 +37,9 @@ class ReactionEvent (
|
||||
fun create(content: String, originalNote: EventInterface, privateKey: ByteArray, createdAt: Long = Date().time / 1000): ReactionEvent {
|
||||
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
||||
|
||||
var tags = listOf( listOf("e", originalNote.id()), listOf("p", originalNote.pubKey()))
|
||||
var tags = listOf(listOf("e", originalNote.id()), listOf("p", originalNote.pubKey()))
|
||||
if (originalNote is LongTextNoteEvent) {
|
||||
tags = tags + listOf( listOf("a", originalNote.address().toTag()) )
|
||||
tags = tags + listOf(listOf("a", originalNote.address().toTag()))
|
||||
}
|
||||
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
|
@@ -2,21 +2,20 @@ package com.vitorpamplona.amethyst.service.model
|
||||
|
||||
import com.vitorpamplona.amethyst.model.HexKey
|
||||
import com.vitorpamplona.amethyst.model.toHexKey
|
||||
import java.util.Date
|
||||
import nostr.postr.Utils
|
||||
import nostr.postr.toHex
|
||||
import java.util.Date
|
||||
|
||||
data class ReportedKey(val key: String, val reportType: ReportEvent.ReportType)
|
||||
|
||||
// NIP 56 event.
|
||||
class ReportEvent (
|
||||
class ReportEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
): Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
private fun defaultReportType(): ReportType {
|
||||
// Works with old and new structures for report.
|
||||
@@ -35,7 +34,7 @@ class ReportEvent (
|
||||
.map {
|
||||
ReportedKey(
|
||||
it[1],
|
||||
it.getOrNull(2)?.toUpperCase()?.let { it1 -> ReportType.valueOf(it1) }?: defaultReportType()
|
||||
it.getOrNull(2)?.toUpperCase()?.let { it1 -> ReportType.valueOf(it1) } ?: defaultReportType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ class ReportEvent (
|
||||
.map {
|
||||
ReportedKey(
|
||||
it[1],
|
||||
it.getOrNull(2)?.toUpperCase()?.let { it1 -> ReportType.valueOf(it1) }?: defaultReportType()
|
||||
it.getOrNull(2)?.toUpperCase()?.let { it1 -> ReportType.valueOf(it1) } ?: defaultReportType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -65,10 +64,10 @@ class ReportEvent (
|
||||
val reportAuthorTag = listOf("p", reportedPost.pubKey(), type.name.lowercase())
|
||||
|
||||
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
||||
var tags:List<List<String>> = listOf(reportPostTag, reportAuthorTag)
|
||||
var tags: List<List<String>> = listOf(reportPostTag, reportAuthorTag)
|
||||
|
||||
if (reportedPost is LongTextNoteEvent) {
|
||||
tags = tags + listOf( listOf("a", reportedPost.address().toTag()) )
|
||||
tags = tags + listOf(listOf("a", reportedPost.address().toTag()))
|
||||
}
|
||||
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
@@ -82,7 +81,7 @@ class ReportEvent (
|
||||
val reportAuthorTag = listOf("p", reportedUser, type.name.lowercase())
|
||||
|
||||
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
||||
val tags:List<List<String>> = listOf(reportAuthorTag)
|
||||
val tags: List<List<String>> = listOf(reportAuthorTag)
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
val sig = Utils.sign(id, privateKey)
|
||||
return ReportEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
|
||||
@@ -95,6 +94,6 @@ class ReportEvent (
|
||||
SPAM,
|
||||
IMPERSONATION,
|
||||
NUDITY,
|
||||
PROFANITY,
|
||||
PROFANITY
|
||||
}
|
||||
}
|
||||
|
@@ -3,18 +3,17 @@ package com.vitorpamplona.amethyst.service.model
|
||||
import com.vitorpamplona.amethyst.model.HexKey
|
||||
import com.vitorpamplona.amethyst.model.toHexKey
|
||||
import com.vitorpamplona.amethyst.service.relays.Client
|
||||
import java.util.Date
|
||||
import nostr.postr.Utils
|
||||
import java.util.Date
|
||||
|
||||
class RepostEvent (
|
||||
class RepostEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
): Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
fun boostedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }
|
||||
fun originalAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) }
|
||||
@@ -41,10 +40,10 @@ class RepostEvent (
|
||||
val replyToAuthor = listOf("p", boostedPost.pubKey())
|
||||
|
||||
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
||||
var tags:List<List<String>> = boostedPost.tags().plus(listOf(replyToPost, replyToAuthor))
|
||||
var tags: List<List<String>> = boostedPost.tags().plus(listOf(replyToPost, replyToAuthor))
|
||||
|
||||
if (boostedPost is LongTextNoteEvent) {
|
||||
tags = tags + listOf( listOf("a", boostedPost.address().toTag()) )
|
||||
tags = tags + listOf(listOf("a", boostedPost.address().toTag()))
|
||||
}
|
||||
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
|
@@ -28,7 +28,7 @@ fun ClickableRoute(
|
||||
val text = user.toBestDisplayName()
|
||||
|
||||
ClickableText(
|
||||
text = AnnotatedString("@${text} "),
|
||||
text = AnnotatedString("@$text "),
|
||||
onClick = { navController.navigate(route) },
|
||||
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
|
||||
)
|
||||
|
@@ -8,28 +8,20 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.text.Paragraph
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -41,9 +33,9 @@ import com.halilibo.richtext.markdown.MarkdownParseOptions
|
||||
import com.halilibo.richtext.ui.RichTextStyle
|
||||
import com.halilibo.richtext.ui.material.MaterialRichText
|
||||
import com.halilibo.richtext.ui.resolveDefaults
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LnInvoiceUtil
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.Nip19
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LnInvoiceUtil
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import java.net.MalformedURLException
|
||||
@@ -79,9 +71,8 @@ fun RichTextViewer(
|
||||
tags: List<List<String>>?,
|
||||
backgroundColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
navController: NavController,
|
||||
navController: NavController
|
||||
) {
|
||||
|
||||
val myMarkDownStyle = RichTextStyle().resolveDefaults().copy(
|
||||
codeBlockStyle = RichTextStyle().resolveDefaults().codeBlockStyle?.copy(
|
||||
textStyle = TextStyle(
|
||||
@@ -113,27 +104,25 @@ fun RichTextViewer(
|
||||
)
|
||||
|
||||
Column(modifier = modifier.animateContentSize()) {
|
||||
|
||||
if ( content.startsWith("# ")
|
||||
|| content.contains("##")
|
||||
|| content.contains("**")
|
||||
|| content.contains("__")
|
||||
|| content.contains("```")
|
||||
if (content.startsWith("# ") ||
|
||||
content.contains("##") ||
|
||||
content.contains("**") ||
|
||||
content.contains("__") ||
|
||||
content.contains("```")
|
||||
) {
|
||||
|
||||
MaterialRichText(
|
||||
style = myMarkDownStyle,
|
||||
style = myMarkDownStyle
|
||||
) {
|
||||
Markdown(
|
||||
content = content,
|
||||
markdownParseOptions = MarkdownParseOptions.Default,
|
||||
markdownParseOptions = MarkdownParseOptions.Default
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// FlowRow doesn't work well with paragraphs. So we need to split them
|
||||
content.split('\n').forEach { paragraph ->
|
||||
FlowRow() {
|
||||
val s = if (isArabic(paragraph)) paragraph.split(' ').reversed() else paragraph.split(' ');
|
||||
val s = if (isArabic(paragraph)) paragraph.split(' ').reversed() else paragraph.split(' ')
|
||||
s.forEach { word: String ->
|
||||
if (canPreview) {
|
||||
// Explicit URL
|
||||
@@ -162,7 +151,7 @@ fun RichTextViewer(
|
||||
} else {
|
||||
Text(
|
||||
text = "$word ",
|
||||
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
|
||||
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -181,7 +170,7 @@ fun RichTextViewer(
|
||||
} else {
|
||||
Text(
|
||||
text = "$word ",
|
||||
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
|
||||
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -196,19 +185,18 @@ private fun isArabic(text: String): Boolean {
|
||||
return text.any { it in '\u0600'..'\u06FF' || it in '\u0750'..'\u077F' }
|
||||
}
|
||||
|
||||
|
||||
fun isBechLink(word: String): Boolean {
|
||||
return word.startsWith("nostr:", true)
|
||||
|| word.startsWith("npub1", true)
|
||||
|| word.startsWith("naddr1", true)
|
||||
|| word.startsWith("note1", true)
|
||||
|| word.startsWith("nprofile1", true)
|
||||
|| word.startsWith("nevent1", true)
|
||||
|| word.startsWith("@npub1", true)
|
||||
|| word.startsWith("@note1", true)
|
||||
|| word.startsWith("@addr1", true)
|
||||
|| word.startsWith("@nprofile1", true)
|
||||
|| word.startsWith("@nevent1", true)
|
||||
return word.startsWith("nostr:", true) ||
|
||||
word.startsWith("npub1", true) ||
|
||||
word.startsWith("naddr1", true) ||
|
||||
word.startsWith("note1", true) ||
|
||||
word.startsWith("nprofile1", true) ||
|
||||
word.startsWith("nevent1", true) ||
|
||||
word.startsWith("@npub1", true) ||
|
||||
word.startsWith("@note1", true) ||
|
||||
word.startsWith("@addr1", true) ||
|
||||
word.startsWith("@nprofile1", true) ||
|
||||
word.startsWith("@nevent1", true)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -218,7 +206,7 @@ fun BechLink(word: String, navController: NavController) {
|
||||
} else if (word.startsWith("@")) {
|
||||
word.replaceFirst("@", "nostr:")
|
||||
} else {
|
||||
"nostr:${word}"
|
||||
"nostr:$word"
|
||||
}
|
||||
|
||||
val nip19Route = try {
|
||||
@@ -234,7 +222,6 @@ fun BechLink(word: String, navController: NavController) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, navController: NavController) {
|
||||
val matcher = tagIndex.matcher(word)
|
||||
@@ -243,7 +230,7 @@ fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgro
|
||||
matcher.find()
|
||||
matcher.group(1).toInt()
|
||||
} catch (e: Exception) {
|
||||
println("Couldn't link tag ${word}")
|
||||
println("Couldn't link tag $word")
|
||||
null
|
||||
}
|
||||
|
||||
@@ -294,7 +281,8 @@ fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgro
|
||||
// if here the tag is not a valid Nostr Hex
|
||||
Text(text = "$word ")
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
Text(text = "$word ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class NIP19ParserTest {
|
||||
assertEquals(30023, address?.kind)
|
||||
assertEquals("d1e60465c2b777325e9133f2100d2bb31416dca810f54a1d95665621c5dee193", address?.pubKeyHex)
|
||||
assertEquals("89de7920", address?.dTag)
|
||||
assertEquals("wss://relay.damus.io" , address?.relay)
|
||||
assertEquals("wss://relay.damus.io", address?.relay)
|
||||
assertEquals("naddr1qqyrswtyv5mnjv3sqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsygx3uczxts4hwue9ayfn7ggq62anzstde2qs749pm9tx2csuthhpjvpsgqqqw4rs8pmj38", address?.toNAddr())
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user