Adds support for NIP-22 exclusive geo posts
This commit is contained in:
@@ -47,8 +47,11 @@ import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
|
||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.ETag
|
||||
import com.vitorpamplona.quartz.encoders.EventHint
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.Nip47WalletConnect
|
||||
import com.vitorpamplona.quartz.encoders.PTag
|
||||
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
||||
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||
@@ -60,6 +63,7 @@ import com.vitorpamplona.quartz.events.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.Contact
|
||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||
import com.vitorpamplona.quartz.events.DeletionEvent
|
||||
@@ -2145,6 +2149,182 @@ class Account(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendReplyComment(
|
||||
message: String,
|
||||
replyingTo: Note,
|
||||
directMentionsUsers: Set<User> = emptySet(),
|
||||
directMentionsNotes: Set<Note> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
wantsToMarkAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
relayList: List<RelaySetupInfo>,
|
||||
draftTag: String? = null,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
|
||||
val usersMentioned =
|
||||
directMentionsUsers
|
||||
.mapTo(HashSet(directMentionsUsers.size)) {
|
||||
PTag(it.pubkeyHex, it.latestMetadataRelay)
|
||||
}
|
||||
|
||||
val addressesMentioned =
|
||||
directMentionsNotes
|
||||
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
|
||||
if (note is AddressableNote) {
|
||||
note.address
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val eventsMentioned =
|
||||
directMentionsNotes
|
||||
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
|
||||
if (note !is AddressableNote) {
|
||||
ETag(note.idHex, note.author?.pubkeyHex, note.relayHintUrl())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
CommentEvent.replyComment(
|
||||
msg = message,
|
||||
replyingTo = EventHint(replyingTo.event as CommentEvent, replyingTo.relayHintUrl()),
|
||||
usersMentioned = usersMentioned,
|
||||
addressesMentioned = addressesMentioned,
|
||||
eventsMentioned = eventsMentioned,
|
||||
nip94attachments = nip94attachments,
|
||||
geohash = geohash,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
isDraft = draftTag != null,
|
||||
signer = signer,
|
||||
) {
|
||||
if (draftTag != null) {
|
||||
if (message.isBlank()) {
|
||||
deleteDraft(draftTag)
|
||||
} else {
|
||||
DraftEvent.create(draftTag, it, signer) { draftEvent ->
|
||||
sendDraftEvent(draftEvent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Client.send(it, relayList = relayList)
|
||||
LocalCache.justConsume(it, null)
|
||||
|
||||
replyingTo.event?.let {
|
||||
Client.send(it, relayList = relayList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendGeoComment(
|
||||
message: String,
|
||||
geohash: String,
|
||||
replyingTo: Note? = null,
|
||||
directMentionsUsers: Set<User> = emptySet(),
|
||||
directMentionsNotes: Set<Note> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
wantsToMarkAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
relayList: List<RelaySetupInfo>,
|
||||
draftTag: String? = null,
|
||||
) {
|
||||
if (!isWriteable()) return
|
||||
|
||||
val usersMentioned =
|
||||
directMentionsUsers
|
||||
.mapTo(HashSet(directMentionsUsers.size)) {
|
||||
PTag(it.pubkeyHex, it.latestMetadataRelay)
|
||||
}
|
||||
|
||||
val addressesMentioned =
|
||||
directMentionsNotes
|
||||
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
|
||||
if (note is AddressableNote) {
|
||||
note.address
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val eventsMentioned =
|
||||
directMentionsNotes
|
||||
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
|
||||
if (note !is AddressableNote) {
|
||||
ETag(note.idHex, note.author?.pubkeyHex, note.relayHintUrl())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (replyingTo != null) {
|
||||
CommentEvent.replyComment(
|
||||
msg = message,
|
||||
replyingTo = EventHint<CommentEvent>(replyingTo.event as CommentEvent, replyingTo.relayHintUrl()),
|
||||
usersMentioned = usersMentioned,
|
||||
addressesMentioned = addressesMentioned,
|
||||
eventsMentioned = eventsMentioned,
|
||||
nip94attachments = nip94attachments,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
isDraft = draftTag != null,
|
||||
signer = signer,
|
||||
) {
|
||||
if (draftTag != null) {
|
||||
if (message.isBlank()) {
|
||||
deleteDraft(draftTag)
|
||||
} else {
|
||||
DraftEvent.create(draftTag, it, signer) { draftEvent ->
|
||||
sendDraftEvent(draftEvent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Client.send(it, relayList = relayList)
|
||||
LocalCache.justConsume(it, null)
|
||||
|
||||
replyingTo.event?.let {
|
||||
Client.send(it, relayList = relayList)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CommentEvent.createGeoComment(
|
||||
msg = message,
|
||||
geohash = geohash,
|
||||
usersMentioned = usersMentioned,
|
||||
addressesMentioned = addressesMentioned,
|
||||
eventsMentioned = eventsMentioned,
|
||||
nip94attachments = nip94attachments,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
isDraft = draftTag != null,
|
||||
signer = signer,
|
||||
) {
|
||||
if (draftTag != null) {
|
||||
if (message.isBlank()) {
|
||||
deleteDraft(draftTag)
|
||||
} else {
|
||||
DraftEvent.create(draftTag, it, signer) { draftEvent ->
|
||||
sendDraftEvent(draftEvent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Client.send(it, relayList = relayList)
|
||||
LocalCache.justConsume(it, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendPost(
|
||||
message: String,
|
||||
replyTo: List<Note>?,
|
||||
|
||||
@@ -62,6 +62,7 @@ import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityListEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||
@@ -818,6 +819,8 @@ object LocalCache {
|
||||
is LongTextNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
|
||||
is GitReplyEvent -> event.tagsWithoutCitations().filter { it != event.repository()?.toTag() }.mapNotNull { checkGetOrCreateNote(it) }
|
||||
is TextNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
|
||||
is CommentEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
|
||||
|
||||
is ChatMessageEvent -> event.taggedEvents().mapNotNull { checkGetOrCreateNote(it) }
|
||||
is LnZapEvent ->
|
||||
event.zappedPost().mapNotNull { checkGetOrCreateNote(it) } +
|
||||
@@ -1632,6 +1635,35 @@ object LocalCache {
|
||||
refreshObservers(note)
|
||||
}
|
||||
|
||||
fun consume(
|
||||
event: CommentEvent,
|
||||
relay: Relay?,
|
||||
) {
|
||||
val note = getOrCreateNote(event.id)
|
||||
val author = getOrCreateUser(event.pubKey)
|
||||
|
||||
if (relay != null) {
|
||||
author.addRelayBeingUsed(relay, event.createdAt)
|
||||
note.addRelay(relay)
|
||||
}
|
||||
|
||||
// Already processed this event.
|
||||
if (note.event != null) return
|
||||
|
||||
if (antiSpam.isSpam(event, relay)) {
|
||||
return
|
||||
}
|
||||
|
||||
val replyTo = computeReplyTo(event)
|
||||
|
||||
note.loadEvent(event, author, replyTo)
|
||||
|
||||
// Counts the replies
|
||||
replyTo.forEach { it.addReply(note) }
|
||||
|
||||
refreshObservers(note)
|
||||
}
|
||||
|
||||
fun consume(
|
||||
event: LiveActivitiesChatMessageEvent,
|
||||
relay: Relay?,
|
||||
@@ -2698,6 +2730,7 @@ object LocalCache {
|
||||
is ChatMessageEvent -> consume(event, relay)
|
||||
is ChatMessageRelayListEvent -> consume(event, relay)
|
||||
is ClassifiedsEvent -> consume(event, relay)
|
||||
is CommentEvent -> consume(event, relay)
|
||||
is CommunityDefinitionEvent -> consume(event, relay)
|
||||
is CommunityListEvent -> consume(event, relay)
|
||||
is CommunityPostApprovalEvent -> {
|
||||
|
||||
@@ -97,7 +97,7 @@ class AddressableNote(
|
||||
return minOf(publishedAt, lastCreatedAt)
|
||||
}
|
||||
|
||||
fun dTag(): String? = (event as? AddressableEvent)?.dTag()
|
||||
fun dTag(): String = address.dTag
|
||||
|
||||
override fun wasOrShouldBeDeletedBy(
|
||||
deletionEvents: Set<HexKey>,
|
||||
@@ -157,13 +157,27 @@ open class Note(
|
||||
host.id,
|
||||
host.pubKey,
|
||||
host.kind,
|
||||
relays.firstOrNull()?.url,
|
||||
relayHintUrl(),
|
||||
)
|
||||
} else {
|
||||
Nip19Bech32.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relays.firstOrNull()?.url)
|
||||
Nip19Bech32.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relayHintUrl())
|
||||
}
|
||||
} else {
|
||||
Nip19Bech32.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relays.firstOrNull()?.url)
|
||||
Nip19Bech32.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relayHintUrl())
|
||||
}
|
||||
}
|
||||
|
||||
fun relayHintUrl(): String? {
|
||||
val authorRelay = author?.latestMetadataRelay
|
||||
|
||||
return if (relays.isNotEmpty()) {
|
||||
if (authorRelay != null && relays.any { it.url == authorRelay }) {
|
||||
authorRelay
|
||||
} else {
|
||||
relays.firstOrNull()?.url
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.vitorpamplona.quartz.events.CalendarRSVPEvent
|
||||
import com.vitorpamplona.quartz.events.CalendarTimeSlotEvent
|
||||
import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||
import com.vitorpamplona.quartz.events.DraftEvent
|
||||
import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent
|
||||
@@ -237,6 +238,7 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
||||
GitIssueEvent.KIND,
|
||||
GitPatchEvent.KIND,
|
||||
HighlightEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
CalendarDateSlotEvent.KIND,
|
||||
CalendarTimeSlotEvent.KIND,
|
||||
CalendarRSVPEvent.KIND,
|
||||
|
||||
@@ -27,8 +27,8 @@ import com.vitorpamplona.quartz.events.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.HighlightEvent
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
@@ -57,12 +57,12 @@ object NostrGeohashDataSource : AmethystNostrDataSource("SingleGeoHashFeed") {
|
||||
ChannelMessageEvent.KIND,
|
||||
LongTextNoteEvent.KIND,
|
||||
PollNoteEvent.KIND,
|
||||
LiveActivitiesChatMessageEvent.KIND,
|
||||
ClassifiedsEvent.KIND,
|
||||
HighlightEvent.KIND,
|
||||
AudioTrackEvent.KIND,
|
||||
AudioHeaderEvent.KIND,
|
||||
WikiNoteEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
),
|
||||
limit = 200,
|
||||
),
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.events.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.HighlightEvent
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.LongTextNoteEvent
|
||||
@@ -66,6 +67,7 @@ object NostrHashtagDataSource : AmethystNostrDataSource("SingleHashtagFeed") {
|
||||
AudioTrackEvent.KIND,
|
||||
AudioHeaderEvent.KIND,
|
||||
WikiNoteEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
),
|
||||
limit = 200,
|
||||
),
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.events.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.events.HighlightEvent
|
||||
@@ -165,7 +166,7 @@ object NostrHomeDataSource : AmethystNostrDataSource("HomeFeed") {
|
||||
HighlightEvent.KIND,
|
||||
AudioHeaderEvent.KIND,
|
||||
AudioTrackEvent.KIND,
|
||||
PinListEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
WikiNoteEvent.KIND,
|
||||
),
|
||||
tags =
|
||||
@@ -204,8 +205,8 @@ object NostrHomeDataSource : AmethystNostrDataSource("HomeFeed") {
|
||||
HighlightEvent.KIND,
|
||||
AudioHeaderEvent.KIND,
|
||||
AudioTrackEvent.KIND,
|
||||
PinListEvent.KIND,
|
||||
WikiNoteEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
),
|
||||
tags =
|
||||
mapOf(
|
||||
@@ -244,6 +245,7 @@ object NostrHomeDataSource : AmethystNostrDataSource("HomeFeed") {
|
||||
PinListEvent.KIND,
|
||||
WikiNoteEvent.KIND,
|
||||
CommunityPostApprovalEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
),
|
||||
tags =
|
||||
mapOf(
|
||||
|
||||
+2
@@ -38,6 +38,7 @@ import com.vitorpamplona.quartz.events.BookmarkListEvent
|
||||
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.events.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.EmojiPackEvent
|
||||
import com.vitorpamplona.quartz.events.HighlightEvent
|
||||
@@ -177,6 +178,7 @@ object NostrSearchEventOrUserDataSource : AmethystNostrDataSource("SearchEventFe
|
||||
PollNoteEvent.KIND,
|
||||
NNSEvent.KIND,
|
||||
WikiNoteEvent.KIND,
|
||||
CommentEvent.KIND,
|
||||
),
|
||||
search = mySearchString,
|
||||
limit = 100,
|
||||
|
||||
@@ -39,29 +39,23 @@ class NewMessageTagger(
|
||||
var dao: Dao,
|
||||
) {
|
||||
val directMentions = mutableSetOf<HexKey>()
|
||||
val directMentionsNotes = mutableSetOf<Note>()
|
||||
val directMentionsUsers = mutableSetOf<User>()
|
||||
|
||||
fun addUserToMentions(user: User) {
|
||||
directMentionsUsers.add(user)
|
||||
directMentions.add(user.pubkeyHex)
|
||||
pTags = if (pTags?.contains(user) == true) pTags else pTags?.plus(user) ?: listOf(user)
|
||||
}
|
||||
|
||||
fun addNoteToReplyTos(note: Note) {
|
||||
directMentionsNotes.add(note)
|
||||
directMentions.add(note.idHex)
|
||||
|
||||
note.author?.let { addUserToMentions(it) }
|
||||
eTags = if (eTags?.contains(note) == true) eTags else eTags?.plus(note) ?: listOf(note)
|
||||
}
|
||||
|
||||
fun tagIndex(user: User): Int {
|
||||
// Postr Events assembles replies before mentions in the tag order
|
||||
return (if (channelHex != null) 1 else 0) + (eTags?.size ?: 0) + (pTags?.indexOf(user) ?: 0)
|
||||
}
|
||||
|
||||
fun tagIndex(note: Note): Int {
|
||||
// Postr Events assembles replies before mentions in the tag order
|
||||
return (if (channelHex != null) 1 else 0) + (eTags?.indexOf(note) ?: 0)
|
||||
}
|
||||
|
||||
suspend fun run() {
|
||||
// adds all references to mentions and reply tos
|
||||
message.split('\n').forEach { paragraph: String ->
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.BaseTextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||
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.Event
|
||||
@@ -162,6 +163,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
// GeoHash
|
||||
var wantsToAddGeoHash by mutableStateOf(false)
|
||||
var location: StateFlow<LocationState.LocationResult>? = null
|
||||
var wantsExclusiveGeoPost by mutableStateOf(false)
|
||||
|
||||
// ZapRaiser
|
||||
var canAddZapRaiser by mutableStateOf(false)
|
||||
@@ -336,7 +338,13 @@ open class NewPostViewModel : ViewModel() {
|
||||
wantsForwardZapTo = localfowardZapTo.isNotEmpty()
|
||||
|
||||
wantsToMarkAsSensitive = draftEvent.tags().any { it.size > 1 && it[0] == "content-warning" }
|
||||
wantsToAddGeoHash = draftEvent.tags().any { it.size > 1 && it[0] == "g" }
|
||||
|
||||
val geohash = draftEvent.getGeoHash()
|
||||
wantsToAddGeoHash = geohash != null
|
||||
if (geohash != null) {
|
||||
wantsExclusiveGeoPost = draftEvent.kind() == CommentEvent.KIND
|
||||
}
|
||||
|
||||
val zapraiser = draftEvent.tags().filter { it.size > 1 && it[0] == "zapraiser" }
|
||||
wantsZapraiser = zapraiser.isNotEmpty()
|
||||
zapRaiserAmount = null
|
||||
@@ -544,7 +552,37 @@ open class NewPostViewModel : ViewModel() {
|
||||
// Doesn't send as nip94 yet because we don't know if it makes sense.
|
||||
// usedAttachments.forEach { account?.sendHeader(it, relayList, {}) }
|
||||
|
||||
if (originalNote?.channelHex() != null) {
|
||||
val replyingTo = originalNote
|
||||
|
||||
if (replyingTo?.event is CommentEvent) {
|
||||
account?.sendReplyComment(
|
||||
message = tagger.message,
|
||||
replyingTo = replyingTo,
|
||||
directMentionsUsers = tagger.directMentionsUsers,
|
||||
directMentionsNotes = tagger.directMentionsNotes,
|
||||
nip94attachments = usedAttachments,
|
||||
geohash = geoHash,
|
||||
zapReceiver = zapReceiver,
|
||||
wantsToMarkAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
relayList = relayList,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else if (wantsExclusiveGeoPost && geoHash != null && (originalNote == null || originalNote?.event is CommentEvent)) {
|
||||
account?.sendGeoComment(
|
||||
message = tagger.message,
|
||||
geohash = geoHash,
|
||||
replyingTo = originalNote,
|
||||
directMentionsUsers = tagger.directMentionsUsers,
|
||||
directMentionsNotes = tagger.directMentionsNotes,
|
||||
nip94attachments = usedAttachments,
|
||||
zapReceiver = zapReceiver,
|
||||
wantsToMarkAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = localZapRaiserAmount,
|
||||
relayList = relayList,
|
||||
draftTag = localDraft,
|
||||
)
|
||||
} else if (originalNote?.channelHex() != null) {
|
||||
if (originalNote is AddressableEvent && originalNote?.address() != null) {
|
||||
account?.sendLiveMessage(
|
||||
message = tagger.message,
|
||||
@@ -946,6 +984,7 @@ open class NewPostViewModel : ViewModel() {
|
||||
wantsForwardZapTo = false
|
||||
wantsToMarkAsSensitive = false
|
||||
wantsToAddGeoHash = false
|
||||
wantsExclusiveGeoPost = false
|
||||
forwardZapTo = Split()
|
||||
forwardZapToEditting = TextFieldValue("")
|
||||
|
||||
|
||||
+2
@@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
@@ -75,6 +76,7 @@ class HomeConversationsFeedFilter(
|
||||
it.event is TextNoteEvent ||
|
||||
it.event is PollNoteEvent ||
|
||||
it.event is ChannelMessageEvent ||
|
||||
it.event is CommentEvent ||
|
||||
it.event is LiveActivitiesChatMessageEvent
|
||||
) &&
|
||||
filterParams.match(it.event) &&
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.events.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.events.HighlightEvent
|
||||
import com.vitorpamplona.quartz.events.LongTextNoteEvent
|
||||
@@ -98,6 +99,7 @@ class HomeNewThreadFeedFilter(
|
||||
(noteEvent is WikiNoteEvent && noteEvent.content.isNotEmpty()) ||
|
||||
noteEvent is PollNoteEvent ||
|
||||
noteEvent is HighlightEvent ||
|
||||
noteEvent is CommentEvent ||
|
||||
noteEvent is AudioTrackEvent ||
|
||||
noteEvent is AudioHeaderEvent
|
||||
) &&
|
||||
|
||||
@@ -304,6 +304,7 @@ fun AppNavigation(
|
||||
val version = it.arguments?.getString("version")
|
||||
val draft = it.arguments?.getString("draft")
|
||||
val enableMessageInterface = it.arguments?.getBoolean("enableMessageInterface") ?: false
|
||||
val enableGeolocation = it.arguments?.getBoolean("enableGeolocation") ?: false
|
||||
|
||||
NewPostScreen(
|
||||
message = draftMessage,
|
||||
@@ -314,6 +315,7 @@ fun AppNavigation(
|
||||
version = version?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
enableMessageInterface = enableMessageInterface,
|
||||
enableGeolocation = enableGeolocation,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
@@ -225,7 +225,7 @@ sealed class Route(
|
||||
|
||||
object NewPost :
|
||||
Route(
|
||||
route = "NewPost?message={message}&attachment={attachment}&baseReplyTo={baseReplyTo}"e={quote}&fork={fork}&version={version}&draft={draft}&enableMessageInterface={enableMessageInterface}",
|
||||
route = "NewPost?message={message}&attachment={attachment}&baseReplyTo={baseReplyTo}"e={quote}&fork={fork}&version={version}&draft={draft}&enableGeolocation={enableGeolocation}&enableMessageInterface={enableMessageInterface}",
|
||||
icon = R.drawable.ic_moments,
|
||||
arguments =
|
||||
listOf(
|
||||
@@ -236,6 +236,7 @@ sealed class Route(
|
||||
navArgument("fork") { type = NavType.StringType },
|
||||
navArgument("version") { type = NavType.StringType },
|
||||
navArgument("draft") { type = NavType.StringType },
|
||||
navArgument("enableGeolocation") { type = NavType.BoolType },
|
||||
navArgument("enableMessageInterface") { type = NavType.BoolType },
|
||||
).toImmutableList(),
|
||||
)
|
||||
@@ -307,6 +308,7 @@ fun buildNewPostRoute(
|
||||
fork: String? = null,
|
||||
version: String? = null,
|
||||
draft: String? = null,
|
||||
enableGeolocation: Boolean = false,
|
||||
enableMessageInterface: Boolean = false,
|
||||
): String =
|
||||
"NewPost?" +
|
||||
@@ -317,4 +319,5 @@ fun buildNewPostRoute(
|
||||
"fork=${fork ?: ""}&" +
|
||||
"version=${version ?: ""}&" +
|
||||
"draft=${draft ?: ""}&" +
|
||||
"enableGeolocation=$enableGeolocation&" +
|
||||
"enableMessageInterface=$enableMessageInterface"
|
||||
|
||||
@@ -157,6 +157,7 @@ import com.vitorpamplona.quartz.events.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.events.DraftEvent
|
||||
@@ -683,6 +684,19 @@ private fun RenderNoteRow(
|
||||
nav,
|
||||
)
|
||||
}
|
||||
is CommentEvent -> {
|
||||
RenderTextEvent(
|
||||
baseNote,
|
||||
makeItShort,
|
||||
canPreview,
|
||||
quotesLeft,
|
||||
unPackReply,
|
||||
backgroundColor,
|
||||
editState,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
is NIP90ContentDiscoveryResponseEvent ->
|
||||
RenderNIP90ContentDiscoveryResponse(
|
||||
baseNote,
|
||||
|
||||
+27
-20
@@ -164,6 +164,7 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapSplitIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.MyTextField
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ShowUserSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
@@ -206,12 +207,14 @@ fun NewPostScreen(
|
||||
fork: Note? = null,
|
||||
version: Note? = null,
|
||||
draft: Note? = null,
|
||||
enableGeolocation: Boolean = false,
|
||||
enableMessageInterface: Boolean = false,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: Nav,
|
||||
) {
|
||||
val postViewModel: NewPostViewModel = viewModel()
|
||||
postViewModel.wantsDirectMessage = enableMessageInterface
|
||||
postViewModel.wantsToAddGeoHash = enableGeolocation
|
||||
|
||||
val context = LocalContext.current
|
||||
val activity = context.getActivity()
|
||||
@@ -693,27 +696,24 @@ fun TakePictureButton(onPictureTaken: (Uri) -> Unit) {
|
||||
},
|
||||
)
|
||||
|
||||
Box {
|
||||
IconButton(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
onClick = {
|
||||
if (cameraPermissionState.status.isGranted) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
imageUri = getPhotoUri(context)
|
||||
imageUri?.let { uri -> launcher.launch(uri) }
|
||||
}
|
||||
} else {
|
||||
cameraPermissionState.launchPermissionRequest()
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (cameraPermissionState.status.isGranted) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
imageUri = getPhotoUri(context)
|
||||
imageUri?.let { uri -> launcher.launch(uri) }
|
||||
}
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CameraAlt,
|
||||
contentDescription = stringRes(id = R.string.upload_image),
|
||||
modifier = Modifier.height(25.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
cameraPermissionState.launchPermissionRequest()
|
||||
}
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CameraAlt,
|
||||
contentDescription = stringRes(id = R.string.upload_image),
|
||||
modifier = Modifier.height(25.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1332,6 +1332,13 @@ fun LocationAsHash(postViewModel: NewPostViewModel) {
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
)
|
||||
|
||||
SettingsRow(
|
||||
R.string.geohash_exclusive,
|
||||
R.string.geohash_exclusive_explainer,
|
||||
) {
|
||||
Switch(postViewModel.wantsExclusiveGeoPost, onCheckedChange = { postViewModel.wantsExclusiveGeoPost = it })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LaunchedEffect(locationPermissionState) { locationPermissionState.launchPermissionRequest() }
|
||||
|
||||
+6
-1
@@ -50,6 +50,7 @@ import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.AROUND_ME
|
||||
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
||||
import com.vitorpamplona.amethyst.service.OnlineChecker
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
@@ -191,7 +192,11 @@ private fun HomePages(
|
||||
}
|
||||
},
|
||||
floatingButton = {
|
||||
NewNoteButton(accountViewModel, nav)
|
||||
val list =
|
||||
accountViewModel.account.settings.defaultHomeFollowList
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
NewNoteButton(nav, list.value == AROUND_ME)
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
|
||||
+2
-3
@@ -35,18 +35,17 @@ import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.buildNewPostRoute
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
|
||||
@Composable
|
||||
fun NewNoteButton(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
enableGeolocation: Boolean = false,
|
||||
) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
val route = buildNewPostRoute()
|
||||
val route = buildNewPostRoute(enableGeolocation = enableGeolocation)
|
||||
nav.nav(route)
|
||||
},
|
||||
modifier = Size55Modifier,
|
||||
|
||||
+13
@@ -169,6 +169,7 @@ import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.events.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.events.CommentEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.events.DraftEvent
|
||||
@@ -572,6 +573,18 @@ private fun FullBleedNoteCompose(
|
||||
RenderDraft(baseNote, 3, true, backgroundColor, accountViewModel, nav)
|
||||
} else if (noteEvent is HighlightEvent) {
|
||||
RenderHighlight(baseNote, false, canPreview, quotesLeft = 3, backgroundColor, accountViewModel, nav)
|
||||
} else if (noteEvent is CommentEvent) {
|
||||
RenderTextEvent(
|
||||
baseNote,
|
||||
false,
|
||||
canPreview,
|
||||
quotesLeft = 3,
|
||||
unPackReply = false,
|
||||
backgroundColor,
|
||||
editState,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
} else if (noteEvent is RepostEvent || noteEvent is GenericRepostEvent) {
|
||||
RenderRepost(baseNote, quotesLeft = 3, backgroundColor, accountViewModel, nav)
|
||||
} else if (noteEvent is TextNoteModificationEvent) {
|
||||
|
||||
@@ -645,6 +645,9 @@
|
||||
<string name="geohash_title">Expose Location as </string>
|
||||
<string name="geohash_explainer">Adds a Geohash of your location to the post. The public will know you are within 5km (3mi) of the current location</string>
|
||||
|
||||
<string name="geohash_exclusive">Location-exclusive Post</string>
|
||||
<string name="geohash_exclusive_explainer">Only followers of the location will see it. Your general followers won\'t see it.</string>
|
||||
|
||||
<string name="loading_location">Loading location</string>
|
||||
<string name="lack_location_permissions">No Location Permissions</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user