sign event in background

This commit is contained in:
greenart7c3
2023-09-04 18:06:09 -03:00
parent 6d691c4741
commit 90aade338c
41 changed files with 727 additions and 1475 deletions
File diff suppressed because it is too large Load Diff
@@ -35,11 +35,29 @@ object AmberUtils {
} }
fun openAmber(event: EventInterface) { fun openAmber(event: EventInterface) {
checkNotInMainThread()
ServiceManager.shouldPauseService = false
content = ""
isActivityRunning = true isActivityRunning = true
openAmber( openAmber(
event.toJson(), event.toJson(),
SignerType.SIGN_EVENT, SignerType.SIGN_EVENT,
IntentUtils.decryptActivityResultLauncher, IntentUtils.activityResultLauncher,
""
)
while (isActivityRunning) {
// do nothing
}
}
fun loginWithAmber() {
checkNotInMainThread()
content = ""
isActivityRunning = true
openAmber(
"",
SignerType.GET_PUBLIC_KEY,
IntentUtils.activityResultLauncher,
"" ""
) )
while (isActivityRunning) { while (isActivityRunning) {
@@ -53,7 +71,7 @@ object AmberUtils {
openAmber( openAmber(
encryptedContent, encryptedContent,
SignerType.NIP04_DECRYPT, SignerType.NIP04_DECRYPT,
IntentUtils.decryptActivityResultLauncher, IntentUtils.activityResultLauncher,
pubKey pubKey
) )
while (isActivityRunning) { while (isActivityRunning) {
@@ -68,7 +86,7 @@ object AmberUtils {
openAmber( openAmber(
decryptedContent, decryptedContent,
SignerType.NIP04_ENCRYPT, SignerType.NIP04_ENCRYPT,
IntentUtils.decryptActivityResultLauncher, IntentUtils.activityResultLauncher,
pubKey pubKey
) )
while (isActivityRunning) { while (isActivityRunning) {
@@ -6,14 +6,8 @@ import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.ServiceManager import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
import com.vitorpamplona.amethyst.service.notifications.RegisterAccounts
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.MainActivity import com.vitorpamplona.amethyst.ui.MainActivity
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.RelayAuthEvent
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@@ -21,64 +15,11 @@ import kotlinx.coroutines.launch
object IntentUtils { object IntentUtils {
lateinit var activityResultLauncher: ActivityResultLauncher<Intent> lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
lateinit var authActivityResultLauncher: ActivityResultLauncher<Intent>
lateinit var decryptActivityResultLauncher: ActivityResultLauncher<Intent>
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
fun start(activity: MainActivity) { fun start(activity: MainActivity) {
activityResultLauncher = activity.registerForActivityResult( activityResultLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode != Activity.RESULT_OK) {
GlobalScope.launch(Dispatchers.Main) {
Toast.makeText(
Amethyst.instance,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@registerForActivityResult
}
val event = it.data?.getStringExtra("event") ?: ""
val signedEvent = Event.fromJson(event)
val authEvent = RelayAuthEvent(signedEvent.id, signedEvent.pubKey, signedEvent.createdAt, signedEvent.tags, signedEvent.content, signedEvent.sig)
RegisterAccounts(LocalPreferences.allSavedAccounts()).postRegistrationEvent(
listOf(authEvent)
)
PushNotificationUtils.hasInit = true
ServiceManager.shouldPauseService = true
}
authActivityResultLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode != Activity.RESULT_OK) {
GlobalScope.launch(Dispatchers.Main) {
Toast.makeText(
Amethyst.instance,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@registerForActivityResult
}
val event = it.data?.getStringExtra("event") ?: ""
val signedEvent = Event.fromJson(event)
val authEvent = RelayAuthEvent(signedEvent.id, signedEvent.pubKey, signedEvent.createdAt, signedEvent.tags, signedEvent.content, signedEvent.sig)
GlobalScope.launch(Dispatchers.IO) {
Client.send(authEvent, authEvent.relay())
}
ServiceManager.shouldPauseService = true
}
decryptActivityResultLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { ) {
if (it.resultCode != Activity.RESULT_OK) { if (it.resultCode != Activity.RESULT_OK) {
GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
@@ -95,6 +36,7 @@ object IntentUtils {
val event = it.data?.getStringExtra("signature") ?: "" val event = it.data?.getStringExtra("signature") ?: ""
AmberUtils.content = event AmberUtils.content = event
AmberUtils.isActivityRunning = false AmberUtils.isActivityRunning = false
ServiceManager.shouldPauseService = true
} }
} }
} }
@@ -8,7 +8,6 @@ import com.vitorpamplona.amethyst.service.relays.EOSEAccount
import com.vitorpamplona.amethyst.service.relays.JsonFilter import com.vitorpamplona.amethyst.service.relays.JsonFilter
import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.service.relays.Relay
import com.vitorpamplona.amethyst.service.relays.TypedFilter import com.vitorpamplona.amethyst.service.relays.TypedFilter
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.events.BadgeAwardEvent import com.vitorpamplona.quartz.events.BadgeAwardEvent
import com.vitorpamplona.quartz.events.BadgeProfilesEvent import com.vitorpamplona.quartz.events.BadgeProfilesEvent
@@ -190,25 +189,13 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
super.auth(relay, challenge) super.auth(relay, challenge)
if (this::account.isInitialized) { if (this::account.isInitialized) {
val loggedInWithAmber = account.loginWithAmber val event = account.createAuthEvent(relay, challenge)
val event = account.createAuthEvent(relay, challenge, loggedInWithAmber)
if (loggedInWithAmber && !account.isWriteable()) { if (event != null) {
if (event != null) { Client.send(
AmberUtils.openAmber( event,
event.toJson(), relay.url
SignerType.SIGN_EVENT, )
IntentUtils.authActivityResultLauncher,
""
)
}
} else {
if (event != null) {
Client.send(
event,
relay.url
)
}
} }
} }
} }
@@ -4,10 +4,7 @@ import android.util.Log
import com.vitorpamplona.amethyst.AccountInfo import com.vitorpamplona.amethyst.AccountInfo
import com.vitorpamplona.amethyst.BuildConfig import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.service.HttpClient import com.vitorpamplona.amethyst.service.HttpClient
import com.vitorpamplona.amethyst.service.IntentUtils
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.quartz.events.RelayAuthEvent import com.vitorpamplona.quartz.events.RelayAuthEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -22,16 +19,14 @@ class RegisterAccounts(
// creates proof that it controls all accounts // creates proof that it controls all accounts
private fun signEventsToProveControlOfAccounts( private fun signEventsToProveControlOfAccounts(
accounts: List<AccountInfo>, accounts: List<AccountInfo>,
notificationToken: String, notificationToken: String
loggedInWithAmber: Boolean
): List<RelayAuthEvent> { ): List<RelayAuthEvent> {
return accounts.mapNotNull { return accounts.mapNotNull {
val acc = LocalPreferences.loadFromEncryptedStorage(it.npub) val acc = LocalPreferences.loadFromEncryptedStorage(it.npub)
if (acc != null) { if (acc != null) {
val relayToUse = acc.activeRelays()?.firstOrNull { it.read } val relayToUse = acc.activeRelays()?.firstOrNull { it.read }
if (relayToUse != null) { if (relayToUse != null) {
val event = acc.createAuthEvent(relayToUse, notificationToken, loggedInWithAmber) acc.createAuthEvent(relayToUse, notificationToken)
event
} else { } else {
null null
} }
@@ -68,25 +63,8 @@ class RegisterAccounts(
} }
suspend fun go(notificationToken: String) = withContext(Dispatchers.IO) { suspend fun go(notificationToken: String) = withContext(Dispatchers.IO) {
val accountsWithoutPrivKey = accounts.filter { !it.hasPrivKey } postRegistrationEvent(
val accountsWithPrivKey = accounts.filter { it.hasPrivKey } signEventsToProveControlOfAccounts(accounts, notificationToken)
accountsWithoutPrivKey.forEach { account -> )
Log.d("fcm register", account.npub)
val events = signEventsToProveControlOfAccounts(listOf(account), notificationToken, account.loggedInWithAmber)
if (events.isNotEmpty()) {
AmberUtils.openAmber(
events.first().toJson(),
SignerType.SIGN_EVENT,
IntentUtils.activityResultLauncher,
""
)
}
}
if (accountsWithPrivKey.isNotEmpty()) {
postRegistrationEvent(
signEventsToProveControlOfAccounts(accountsWithPrivKey, notificationToken, false)
)
}
} }
} }
@@ -75,14 +75,12 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.rememberPermissionState
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.ServersAvailable import com.vitorpamplona.amethyst.model.ServersAvailable
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil
import com.vitorpamplona.amethyst.service.noProtocolUrlValidator import com.vitorpamplona.amethyst.service.noProtocolUrlValidator
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.components.* import com.vitorpamplona.amethyst.ui.components.*
import com.vitorpamplona.amethyst.ui.note.CancelIcon import com.vitorpamplona.amethyst.ui.note.CancelIcon
import com.vitorpamplona.amethyst.ui.note.CloseIcon import com.vitorpamplona.amethyst.ui.note.CloseIcon
@@ -105,7 +103,6 @@ import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.replyModifier import com.vitorpamplona.amethyst.ui.theme.replyModifier
import com.vitorpamplona.amethyst.ui.theme.subtleBorder import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.toImmutableListOfLists import com.vitorpamplona.quartz.events.toImmutableListOfLists
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -162,25 +159,6 @@ fun NewPostView(
} }
} }
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent, relayList = relayList)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
onClose()
}
},
data = event!!.toJson()
)
}
Dialog( Dialog(
onDismissRequest = { onClose() }, onDismissRequest = { onClose() },
properties = DialogProperties( properties = DialogProperties(
@@ -234,10 +212,8 @@ fun NewPostView(
PostButton( PostButton(
onPost = { onPost = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = postViewModel.sendPost(relayList = relayList, !accountViewModel.loggedInWithAmber()) postViewModel.sendPost(relayList = relayList)
if (!accountViewModel.loggedInWithAmber()) { onClose()
onClose()
}
} }
}, },
isActive = postViewModel.canPost() isActive = postViewModel.canPost()
@@ -14,7 +14,11 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.fonfon.kgeohash.toGeoHash import com.fonfon.kgeohash.toGeoHash
import com.vitorpamplona.amethyst.model.* import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.ServersAvailable
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.FileHeader import com.vitorpamplona.amethyst.service.FileHeader
import com.vitorpamplona.amethyst.service.LocationUtil import com.vitorpamplona.amethyst.service.LocationUtil
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
@@ -27,7 +31,6 @@ import com.vitorpamplona.quartz.events.AddressableEvent
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.CommunityDefinitionEvent import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.PrivateDmEvent import com.vitorpamplona.quartz.events.PrivateDmEvent
import com.vitorpamplona.quartz.events.TextNoteEvent import com.vitorpamplona.quartz.events.TextNoteEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -158,7 +161,7 @@ open class NewPostViewModel() : ViewModel() {
this.account = account this.account = account
} }
fun sendPost(relayList: List<Relay>? = null, signEvent: Boolean): Event? { fun sendPost(relayList: List<Relay>? = null) {
try { try {
val tagger = NewMessageTagger(message.text, mentions, replyTos, originalNote?.channelHex()) val tagger = NewMessageTagger(message.text, mentions, replyTos, originalNote?.channelHex())
tagger.run() tagger.run()
@@ -188,13 +191,12 @@ open class NewPostViewModel() : ViewModel() {
if (originalNote?.channelHex() != null) { if (originalNote?.channelHex() != null) {
if (originalNote is AddressableEvent && originalNote?.address() != null) { if (originalNote is AddressableEvent && originalNote?.address() != null) {
return account?.sendLiveMessage(tagger.message, originalNote?.address()!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount, geoHash, signEvent) account?.sendLiveMessage(tagger.message, originalNote?.address()!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount, geoHash)
} else { } else {
return account?.sendChannelMessage(tagger.message, tagger.channelHex!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount, geoHash, signEvent) account?.sendChannelMessage(tagger.message, tagger.channelHex!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount, geoHash)
} }
} else if (originalNote?.event is PrivateDmEvent) { } else if (originalNote?.event is PrivateDmEvent) {
account?.sendPrivateMessage(tagger.message, originalNote!!.author!!, originalNote!!, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount, geoHash) account?.sendPrivateMessage(tagger.message, originalNote!!.author!!, originalNote!!, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount, geoHash)
return null
} else if (originalNote?.event is ChatMessageEvent) { } else if (originalNote?.event is ChatMessageEvent) {
val receivers = (originalNote?.event as ChatMessageEvent).recipientsPubKey().plus(originalNote?.author?.pubkeyHex).filterNotNull().toSet().toList() val receivers = (originalNote?.event as ChatMessageEvent).recipientsPubKey().plus(originalNote?.author?.pubkeyHex).filterNotNull().toSet().toList()
@@ -207,10 +209,8 @@ open class NewPostViewModel() : ViewModel() {
wantsToMarkAsSensitive = wantsToMarkAsSensitive, wantsToMarkAsSensitive = wantsToMarkAsSensitive,
zapReceiver = zapReceiver, zapReceiver = zapReceiver,
zapRaiserAmount = localZapRaiserAmount, zapRaiserAmount = localZapRaiserAmount,
geohash = geoHash, geohash = geoHash
signEvent = signEvent
) )
return null
} else if (!dmUsers.isNullOrEmpty()) { } else if (!dmUsers.isNullOrEmpty()) {
if (nip24 || dmUsers.size > 1) { if (nip24 || dmUsers.size > 1) {
account?.sendNIP24PrivateMessage( account?.sendNIP24PrivateMessage(
@@ -222,10 +222,8 @@ open class NewPostViewModel() : ViewModel() {
wantsToMarkAsSensitive = wantsToMarkAsSensitive, wantsToMarkAsSensitive = wantsToMarkAsSensitive,
zapReceiver = zapReceiver, zapReceiver = zapReceiver,
zapRaiserAmount = localZapRaiserAmount, zapRaiserAmount = localZapRaiserAmount,
geohash = geoHash, geohash = geoHash
signEvent = signEvent
) )
return null
} else { } else {
account?.sendPrivateMessage( account?.sendPrivateMessage(
message = tagger.message, message = tagger.message,
@@ -235,14 +233,12 @@ open class NewPostViewModel() : ViewModel() {
wantsToMarkAsSensitive = wantsToMarkAsSensitive, wantsToMarkAsSensitive = wantsToMarkAsSensitive,
zapReceiver = zapReceiver, zapReceiver = zapReceiver,
zapRaiserAmount = localZapRaiserAmount, zapRaiserAmount = localZapRaiserAmount,
geohash = geoHash, geohash = geoHash
signEvent = signEvent
) )
return null
} }
} else { } else {
if (wantsPoll) { if (wantsPoll) {
return account?.sendPoll( account?.sendPoll(
tagger.message, tagger.message,
tagger.replyTos, tagger.replyTos,
tagger.mentions, tagger.mentions,
@@ -255,8 +251,7 @@ open class NewPostViewModel() : ViewModel() {
wantsToMarkAsSensitive, wantsToMarkAsSensitive,
localZapRaiserAmount, localZapRaiserAmount,
relayList, relayList,
geoHash, geoHash
signEvent
) )
} else { } else {
// adds markers // adds markers
@@ -266,7 +261,7 @@ open class NewPostViewModel() : ViewModel() {
?: originalNote?.replyTo?.firstOrNull()?.idHex // old rules, first item is root. ?: originalNote?.replyTo?.firstOrNull()?.idHex // old rules, first item is root.
val replyId = originalNote?.idHex val replyId = originalNote?.idHex
return account?.sendPost( account?.sendPost(
message = tagger.message, message = tagger.message,
replyTo = tagger.replyTos, replyTo = tagger.replyTos,
mentions = tagger.mentions, mentions = tagger.mentions,
@@ -278,8 +273,7 @@ open class NewPostViewModel() : ViewModel() {
root = rootId, root = rootId,
directMentions = tagger.directMentions, directMentions = tagger.directMentions,
relayList = relayList, relayList = relayList,
geohash = geoHash, geohash = geoHash
signEvent = signEvent
) )
} }
} }
@@ -56,10 +56,8 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.RelayInformation import com.vitorpamplona.amethyst.model.RelayInformation
import com.vitorpamplona.amethyst.model.RelaySetupInfo import com.vitorpamplona.amethyst.model.RelaySetupInfo
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.service.relays.Constants.defaultRelays import com.vitorpamplona.amethyst.service.relays.Constants.defaultRelays
import com.vitorpamplona.amethyst.service.relays.FeedType import com.vitorpamplona.amethyst.service.relays.FeedType
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -69,9 +67,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.Event
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.Math.round import java.lang.Math.round
@@ -91,26 +87,6 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
} }
} }
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
postViewModel.clear()
onClose()
}
},
data = event!!.toJson()
)
}
Dialog( Dialog(
onDismissRequest = onClose, onDismissRequest = onClose,
properties = DialogProperties(usePlatformDefaultWidth = false) properties = DialogProperties(usePlatformDefaultWidth = false)
@@ -144,12 +120,8 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
PostButton( PostButton(
onPost = { onPost = {
if (accountViewModel.loggedInWithAmber()) { postViewModel.create()
event = postViewModel.create(false) onClose()
} else {
postViewModel.create(true)
onClose()
}
}, },
true true
) )
@@ -26,20 +26,14 @@ class NewRelayListViewModel : ViewModel() {
clear() clear()
} }
fun create(signEvent: Boolean): ContactListEvent? { fun create() {
if (!signEvent) {
relays.let {
return account.saveRelayList(it.value, false)
}
}
relays.let { relays.let {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
account.saveRelayList(it.value, true) account.saveRelayList(it.value)
} }
} }
clear() clear()
return null
} }
fun clear() { fun clear() {
@@ -17,11 +17,7 @@ import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -33,10 +29,7 @@ import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.Event
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -70,26 +63,6 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
Column( Column(
modifier = Modifier.padding(10.dp) modifier = Modifier.padding(10.dp)
) { ) {
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
postViewModel.clear()
onClose()
}
},
data = event!!.toJson()
)
}
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
@@ -103,12 +76,8 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
PostButton( PostButton(
onPost = { onPost = {
if (account.loginWithAmber) { postViewModel.create()
event = postViewModel.create(false) onClose()
} else {
postViewModel.create(true)
onClose()
}
}, },
true true
) )
@@ -13,7 +13,6 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.components.MediaCompressor import com.vitorpamplona.amethyst.ui.components.MediaCompressor
import com.vitorpamplona.quartz.events.GitHubIdentity import com.vitorpamplona.quartz.events.GitHubIdentity
import com.vitorpamplona.quartz.events.MastodonIdentity import com.vitorpamplona.quartz.events.MastodonIdentity
import com.vitorpamplona.quartz.events.MetadataEvent
import com.vitorpamplona.quartz.events.TwitterIdentity import com.vitorpamplona.quartz.events.TwitterIdentity
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@@ -73,7 +72,7 @@ class NewUserMetadataViewModel : ViewModel() {
} }
} }
fun create(signEvent: Boolean): MetadataEvent? { fun create() {
// Tries to not delete any existing attribute that we do not work with. // Tries to not delete any existing attribute that we do not work with.
val latest = account.userProfile().info?.latestMetadata val latest = account.userProfile().info?.latestMetadata
val currentJson = if (latest != null) { val currentJson = if (latest != null) {
@@ -122,16 +121,10 @@ class NewUserMetadataViewModel : ViewModel() {
val writer = StringWriter() val writer = StringWriter()
ObjectMapper().writeValue(writer, currentJson) ObjectMapper().writeValue(writer, currentJson)
if (signEvent) { viewModelScope.launch(Dispatchers.IO) {
viewModelScope.launch(Dispatchers.IO) { account.sendNewUserMetadata(writer.buffer.toString(), newClaims)
account.sendNewUserMetadata(writer.buffer.toString(), newClaims, true)
}
clear()
} else {
return account.sendNewUserMetadata(writer.buffer.toString(), newClaims, false)
} }
clear()
return null
} }
fun clear() { fun clear() {
@@ -30,7 +30,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
@@ -41,8 +40,6 @@ import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.TextNoteEvent
import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
enum class SignerType { enum class SignerType {
@@ -189,15 +186,3 @@ fun SignerDialog(
} }
} }
} }
@Preview
@Composable
fun Test() {
SignerDialog(
onClose = { },
onPost = { },
data = TextNoteEvent("", "", TimeUtils.now(), emptyList(), "test", "").toJson(),
type = SignerType.SIGN_EVENT,
pubKey = ""
)
}
@@ -17,7 +17,6 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -33,16 +32,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.CloseButton import com.vitorpamplona.amethyst.ui.actions.CloseButton
import com.vitorpamplona.amethyst.ui.actions.PostButton import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.TextNoteEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -57,11 +51,11 @@ class AddBountyAmountViewModel : ViewModel() {
this.bounty = bounty this.bounty = bounty
} }
fun sendPost(signEvent: Boolean): TextNoteEvent? { fun sendPost() {
val newValue = nextAmount.text.trim().toLongOrNull() val newValue = nextAmount.text.trim().toLongOrNull()
if (newValue != null) { if (newValue != null) {
val event = account?.sendPost( account?.sendPost(
message = newValue.toString(), message = newValue.toString(),
replyTo = listOfNotNull(bounty), replyTo = listOfNotNull(bounty),
mentions = listOfNotNull(bounty?.author), mentions = listOfNotNull(bounty?.author),
@@ -69,14 +63,11 @@ class AddBountyAmountViewModel : ViewModel() {
wantsToMarkAsSensitive = false, wantsToMarkAsSensitive = false,
replyingTo = null, replyingTo = null,
root = null, root = null,
directMentions = setOf(), directMentions = setOf()
signEvent = signEvent
) )
nextAmount = TextFieldValue("") nextAmount = TextFieldValue("")
return event
} }
return null
} }
fun cancel() { fun cancel() {
@@ -94,25 +85,6 @@ fun AddBountyAmountDialog(bounty: Note, accountViewModel: AccountViewModel, onCl
postViewModel.load(accountViewModel.account, bounty) postViewModel.load(accountViewModel.account, bounty)
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
onClose()
}
},
data = event!!.toJson()
)
}
Dialog( Dialog(
onDismissRequest = { onClose() }, onDismissRequest = { onClose() },
properties = DialogProperties( properties = DialogProperties(
@@ -138,8 +110,8 @@ fun AddBountyAmountDialog(bounty: Note, accountViewModel: AccountViewModel, onCl
PostButton( PostButton(
onPost = { onPost = {
event = postViewModel.sendPost(!accountViewModel.loggedInWithAmber()) scope.launch(Dispatchers.IO) {
if (!accountViewModel.loggedInWithAmber()) { postViewModel.sendPost()
onClose() onClose()
} }
}, },
@@ -37,11 +37,9 @@ import androidx.compose.material.icons.filled.Report
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
@@ -62,17 +60,13 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.components.SelectTextDialog import com.vitorpamplona.amethyst.ui.components.SelectTextDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog
import com.vitorpamplona.amethyst.ui.theme.WarningColor import com.vitorpamplona.amethyst.ui.theme.WarningColor
import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground
import com.vitorpamplona.quartz.events.AudioTrackEvent import com.vitorpamplona.quartz.events.AudioTrackEvent
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -206,25 +200,6 @@ private fun RenderMainPopup(
val isOwnNote = accountViewModel.isLoggedUser(note.author) val isOwnNote = accountViewModel.isLoggedUser(note.author)
val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author) val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author)
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
onDismiss()
}
},
data = event!!.toJson()
)
}
Popup(onDismissRequest = onDismiss, alignment = Alignment.Center) { Popup(onDismissRequest = onDismiss, alignment = Alignment.Center) {
Card( Card(
modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape), modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape),
@@ -300,10 +275,8 @@ private fun RenderMainPopup(
) { ) {
if (accountViewModel.hideDeleteRequestDialog) { if (accountViewModel.hideDeleteRequestDialog) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) accountViewModel.delete(note)
if (!accountViewModel.loggedInWithAmber()) { onDismiss()
onDismiss()
}
} }
} else { } else {
showDeleteAlertDialog.value = true showDeleteAlertDialog.value = true
@@ -315,10 +288,8 @@ private fun RenderMainPopup(
stringResource(R.string.quick_action_unfollow) stringResource(R.string.quick_action_unfollow)
) { ) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.unfollow(note.author!!, !accountViewModel.loggedInWithAmber()) accountViewModel.unfollow(note.author!!)
if (!accountViewModel.loggedInWithAmber()) { onDismiss()
onDismiss()
}
} }
} }
} else { } else {
@@ -327,10 +298,8 @@ private fun RenderMainPopup(
stringResource(R.string.quick_action_follow) stringResource(R.string.quick_action_follow)
) { ) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.follow(note.author!!, !accountViewModel.loggedInWithAmber()) accountViewModel.follow(note.author!!)
if (!accountViewModel.loggedInWithAmber()) { onDismiss()
onDismiss()
}
} }
} }
} }
@@ -413,25 +382,6 @@ fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) {
fun DeleteAlertDialog(note: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit) { fun DeleteAlertDialog(note: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
onDismiss()
}
},
data = event!!.toJson()
)
}
QuickActionAlertDialog( QuickActionAlertDialog(
title = stringResource(R.string.quick_action_request_deletion_alert_title), title = stringResource(R.string.quick_action_request_deletion_alert_title),
textContent = stringResource(R.string.quick_action_request_deletion_alert_body), textContent = stringResource(R.string.quick_action_request_deletion_alert_body),
@@ -439,20 +389,16 @@ fun DeleteAlertDialog(note: Note, accountViewModel: AccountViewModel, onDismiss:
buttonText = stringResource(R.string.quick_action_delete_dialog_btn), buttonText = stringResource(R.string.quick_action_delete_dialog_btn),
onClickDoOnce = { onClickDoOnce = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) accountViewModel.delete(note)
}
if (!accountViewModel.loggedInWithAmber()) {
onDismiss()
} }
onDismiss()
}, },
onClickDontShowAgain = { onClickDontShowAgain = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) accountViewModel.delete(note)
accountViewModel.dontShowDeleteRequestDialog() accountViewModel.dontShowDeleteRequestDialog()
} }
if (!accountViewModel.loggedInWithAmber()) { onDismiss()
onDismiss()
}
}, },
onDismiss = onDismiss onDismiss = onDismiss
) )
@@ -77,11 +77,8 @@ import coil.compose.AsyncImage
import coil.request.CachePolicy import coil.request.CachePolicy
import coil.request.ImageRequest import coil.request.ImageRequest
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.NewPostView import com.vitorpamplona.amethyst.ui.actions.NewPostView
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.components.ImageUrlType import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.TextType import com.vitorpamplona.amethyst.ui.components.TextType
@@ -110,7 +107,6 @@ import com.vitorpamplona.amethyst.ui.theme.TinyBorders
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter
import com.vitorpamplona.quartz.events.Event
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -647,26 +643,6 @@ fun BoostReaction(
val iconButtonModifier = remember { val iconButtonModifier = remember {
Modifier.size(iconSize) Modifier.size(iconSize)
} }
var event by remember {
mutableStateOf<Event?>(null)
}
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
IconButton( IconButton(
modifier = iconButtonModifier, modifier = iconButtonModifier,
@@ -674,7 +650,7 @@ fun BoostReaction(
if (accountViewModel.isWriteable()) { if (accountViewModel.isWriteable()) {
if (accountViewModel.hasBoosted(baseNote)) { if (accountViewModel.hasBoosted(baseNote)) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.deleteBoostsTo(baseNote, true) accountViewModel.deleteBoostsTo(baseNote)
} }
} else { } else {
wantsToBoost = true wantsToBoost = true
@@ -683,7 +659,7 @@ fun BoostReaction(
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
if (accountViewModel.hasBoosted(baseNote)) { if (accountViewModel.hasBoosted(baseNote)) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.deleteBoostsTo(baseNote, false) accountViewModel.deleteBoostsTo(baseNote)
} }
} else { } else {
wantsToBoost = true wantsToBoost = true
@@ -716,7 +692,7 @@ fun BoostReaction(
}, },
onRepost = { onRepost = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.boost(baseNote, false) accountViewModel.boost(baseNote)
} }
} }
) )
@@ -766,24 +742,6 @@ fun LikeReaction(
var wantsToChangeReactionSymbol by remember { mutableStateOf(false) } var wantsToChangeReactionSymbol by remember { mutableStateOf(false) }
var wantsToReact by remember { mutableStateOf(false) } var wantsToReact by remember { mutableStateOf(false) }
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
Box( Box(
contentAlignment = Center, contentAlignment = Center,
@@ -804,9 +762,9 @@ fun LikeReaction(
if (accountViewModel.account.reactionChoices.size == 1) { if (accountViewModel.account.reactionChoices.size == 1) {
val reaction = accountViewModel.account.reactionChoices.first() val reaction = accountViewModel.account.reactionChoices.first()
if (accountViewModel.hasReactedTo(baseNote, reaction)) { if (accountViewModel.hasReactedTo(baseNote, reaction)) {
event = accountViewModel.deleteReactionTo(baseNote, reaction, false) accountViewModel.deleteReactionTo(baseNote, reaction)
} else { } else {
event = accountViewModel.reactTo(baseNote, reaction, false) accountViewModel.reactTo(baseNote, reaction)
} }
} else if (accountViewModel.account.reactionChoices.size > 1) { } else if (accountViewModel.account.reactionChoices.size > 1) {
wantsToReact = true wantsToReact = true
@@ -961,9 +919,9 @@ private fun likeClick(
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
val reaction = accountViewModel.account.reactionChoices.first() val reaction = accountViewModel.account.reactionChoices.first()
if (accountViewModel.hasReactedTo(baseNote, reaction)) { if (accountViewModel.hasReactedTo(baseNote, reaction)) {
accountViewModel.deleteReactionTo(baseNote, reaction, true) accountViewModel.deleteReactionTo(baseNote, reaction)
} else { } else {
accountViewModel.reactTo(baseNote, reaction, true) accountViewModel.reactTo(baseNote, reaction)
} }
} }
} else if (accountViewModel.account.reactionChoices.size > 1) { } else if (accountViewModel.account.reactionChoices.size > 1) {
@@ -1294,7 +1252,7 @@ private fun BoostTypeChoicePopup(baseNote: Note, iconSize: Dp, accountViewModel:
onClick = { onClick = {
if (accountViewModel.isWriteable()) { if (accountViewModel.isWriteable()) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.boost(baseNote, true) accountViewModel.boost(baseNote)
onDismiss() onDismiss()
} }
} else { } else {
@@ -1377,40 +1335,16 @@ private fun ActionableReactionButton(
toRemove: Set<String> toRemove: Set<String>
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
onDismiss()
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
onDismiss()
}
},
data = event!!.toJson()
)
}
Button( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
val loggedInWithAmber = accountViewModel.loggedInWithAmber()
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.reactToOrDelete( accountViewModel.reactToOrDelete(
baseNote, baseNote,
reactionType, reactionType
!loggedInWithAmber
) )
if (!loggedInWithAmber) { onDismiss()
onDismiss()
}
} }
}, },
shape = ButtonBorder, shape = ButtonBorder,
@@ -1422,16 +1356,12 @@ private fun ActionableReactionButton(
val thisModifier = remember(reactionType) { val thisModifier = remember(reactionType) {
Modifier.combinedClickable( Modifier.combinedClickable(
onClick = { onClick = {
val loggedInWithAmber = accountViewModel.loggedInWithAmber()
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.reactToOrDelete( accountViewModel.reactToOrDelete(
baseNote, baseNote,
reactionType, reactionType
!loggedInWithAmber
) )
if (!loggedInWithAmber) { onDismiss()
onDismiss()
}
} }
}, },
onLongClick = { onLongClick = {
@@ -41,19 +41,15 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map import androidx.lifecycle.map
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.AmberUtils import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.Event
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -388,31 +384,12 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
onDismiss()
}
},
data = event!!.toJson()
)
}
if (!state.isFollowingAuthor) { if (!state.isFollowingAuthor) {
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
val author = note.author ?: return@DropdownMenuItem val author = note.author ?: return@DropdownMenuItem
event = accountViewModel.follow(author, !accountViewModel.loggedInWithAmber()) scope.launch(Dispatchers.IO) {
if (!accountViewModel.loggedInWithAmber()) { accountViewModel.follow(author)
onDismiss() onDismiss()
} }
} }
@@ -479,7 +456,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
) )
bookmarks?.decryptedContent = AmberUtils.content bookmarks?.decryptedContent = AmberUtils.content
AmberUtils.content = "" AmberUtils.content = ""
event = accountViewModel.removePrivateBookmark(note, bookmarks?.decryptedContent ?: "") accountViewModel.removePrivateBookmark(note, bookmarks?.decryptedContent ?: "")
} else { } else {
accountViewModel.removePrivateBookmark(note) accountViewModel.removePrivateBookmark(note)
onDismiss() onDismiss()
@@ -501,7 +478,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
) )
bookmarks?.decryptedContent = AmberUtils.content bookmarks?.decryptedContent = AmberUtils.content
AmberUtils.content = "" AmberUtils.content = ""
event = accountViewModel.addPrivateBookmark(note, bookmarks?.decryptedContent ?: "") accountViewModel.addPrivateBookmark(note, bookmarks?.decryptedContent ?: "")
} else { } else {
accountViewModel.addPrivateBookmark(note) accountViewModel.addPrivateBookmark(note)
onDismiss() onDismiss()
@@ -524,7 +501,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
) )
bookmarks?.decryptedContent = AmberUtils.content bookmarks?.decryptedContent = AmberUtils.content
AmberUtils.content = "" AmberUtils.content = ""
event = accountViewModel.removePublicBookmark( accountViewModel.removePublicBookmark(
note, note,
bookmarks?.decryptedContent ?: "" bookmarks?.decryptedContent ?: ""
) )
@@ -549,7 +526,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
) )
bookmarks?.decryptedContent = AmberUtils.content bookmarks?.decryptedContent = AmberUtils.content
AmberUtils.content = "" AmberUtils.content = ""
event = accountViewModel.addPublicBookmark( accountViewModel.addPublicBookmark(
note, note,
bookmarks?.decryptedContent ?: "" bookmarks?.decryptedContent ?: ""
) )
@@ -572,10 +549,8 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) accountViewModel.delete(note)
if (!accountViewModel.loggedInWithAmber()) { onDismiss()
onDismiss()
}
} }
} }
) { ) {
@@ -1,9 +1,6 @@
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import android.app.Activity
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -29,14 +26,9 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.FollowButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.FollowButton
@@ -47,9 +39,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.showAmountAxis
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.Size55dp import com.vitorpamplona.amethyst.ui.theme.Size55dp
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.ContactListEvent
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -185,113 +174,11 @@ fun UserActionOptions(
accountViewModel: AccountViewModel accountViewModel: AccountViewModel
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
accountViewModel.account.live.invalidateData()
accountViewModel.account.saveable.invalidateData()
event = null
}
},
data = event!!.toJson()
)
}
val encryptResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch {
Toast.makeText(
context,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@rememberLauncherForActivityResult
}
val encryptedContent = it.data?.getStringExtra("signature") ?: ""
event = accountViewModel.show(baseAuthor, encryptedContent)
}
)
val decryptResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch {
Toast.makeText(
context,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@rememberLauncherForActivityResult
}
val decryptedContent = it.data?.getStringExtra("signature") ?: ""
val blockList = accountViewModel.account.getBlockList()
val privateTags = if (blockList == null) {
listOf(listOf("p", baseAuthor.pubkeyHex))
} else {
if (accountViewModel.account.isHidden(baseAuthor)) {
blockList.privateTagsOrEmpty(decryptedContent).filter { element -> !element.contains(baseAuthor.pubkeyHex) }
} else {
blockList.privateTagsOrEmpty(decryptedContent).plus(element = listOf("p", baseAuthor.pubkeyHex))
}
}
val msg = Event.mapper.writeValueAsString(privateTags)
ServiceManager.shouldPauseService = true
AmberUtils.openAmber(
msg,
SignerType.NIP04_ENCRYPT,
encryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
}
)
WatchIsHiddenUser(baseAuthor, accountViewModel) { isHidden -> WatchIsHiddenUser(baseAuthor, accountViewModel) { isHidden ->
if (isHidden) { if (isHidden) {
ShowUserButton { ShowUserButton {
if (accountViewModel.loggedInWithAmber()) { accountViewModel.show(baseAuthor)
scope.launch(Dispatchers.IO) {
val blockList = accountViewModel.account.getBlockList()
val content = blockList?.content ?: ""
if (content.isBlank()) {
val privateTags = listOf(listOf("p", baseAuthor.pubkeyHex))
val msg = Event.mapper.writeValueAsString(privateTags)
AmberUtils.openAmber(
msg,
SignerType.NIP04_ENCRYPT,
encryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
} else {
AmberUtils.openAmber(
content,
SignerType.NIP04_DECRYPT,
decryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
}
}
} else {
accountViewModel.show(baseAuthor)
}
} }
} else { } else {
ShowFollowingOrUnfollowingButton(baseAuthor, accountViewModel) ShowFollowingOrUnfollowingButton(baseAuthor, accountViewModel)
@@ -321,30 +208,13 @@ fun ShowFollowingOrUnfollowingButton(
} }
} }
var event by remember { mutableStateOf<ContactListEvent?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
if (isFollowing) { if (isFollowing) {
UnfollowButton { UnfollowButton {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.unfollow(baseAuthor, false) scope.launch(Dispatchers.IO) {
accountViewModel.unfollow(baseAuthor)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -358,7 +228,7 @@ fun ShowFollowingOrUnfollowingButton(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.unfollow(baseAuthor, true) accountViewModel.unfollow(baseAuthor)
} }
} }
} }
@@ -366,7 +236,9 @@ fun ShowFollowingOrUnfollowingButton(
FollowButton { FollowButton {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.follow(baseAuthor, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.follow(baseAuthor)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -380,7 +252,7 @@ fun ShowFollowingOrUnfollowingButton(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.follow(baseAuthor, true) accountViewModel.follow(baseAuthor)
} }
} }
} }
@@ -30,16 +30,11 @@ import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification
import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus
import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.BookmarkListEvent
import com.vitorpamplona.quartz.events.ContactListEvent
import com.vitorpamplona.quartz.events.DeletionEvent
import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.GiftWrapEvent import com.vitorpamplona.quartz.events.GiftWrapEvent
import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent
import com.vitorpamplona.quartz.events.PayInvoiceErrorResponse import com.vitorpamplona.quartz.events.PayInvoiceErrorResponse
import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.events.ReactionEvent
import com.vitorpamplona.quartz.events.ReportEvent import com.vitorpamplona.quartz.events.ReportEvent
import com.vitorpamplona.quartz.events.SealedGossipEvent import com.vitorpamplona.quartz.events.SealedGossipEvent
import com.vitorpamplona.quartz.events.UserMetadata import com.vitorpamplona.quartz.events.UserMetadata
@@ -112,16 +107,16 @@ class AccountViewModel(val account: Account) : ViewModel() {
return account.userProfile() return account.userProfile()
} }
fun reactTo(note: Note, reaction: String, signEvent: Boolean): ReactionEvent? { fun reactTo(note: Note, reaction: String) {
return account.reactTo(note, reaction, signEvent) account.reactTo(note, reaction)
} }
fun reactToOrDelete(note: Note, reaction: String, signEvent: Boolean): Event? { fun reactToOrDelete(note: Note, reaction: String) {
val currentReactions = account.reactionTo(note, reaction) val currentReactions = account.reactionTo(note, reaction)
if (currentReactions.isNotEmpty()) { if (currentReactions.isNotEmpty()) {
return account.delete(currentReactions, signEvent) account.delete(currentReactions)
} else { } else {
return account.reactTo(note, reaction, signEvent) account.reactTo(note, reaction)
} }
} }
@@ -134,16 +129,16 @@ class AccountViewModel(val account: Account) : ViewModel() {
return account.hasReacted(baseNote, reaction) return account.hasReacted(baseNote, reaction)
} }
fun deleteReactionTo(note: Note, reaction: String, signEvent: Boolean): DeletionEvent? { fun deleteReactionTo(note: Note, reaction: String) {
return account.delete(account.reactionTo(note, reaction), signEvent) account.delete(account.reactionTo(note, reaction))
} }
fun hasBoosted(baseNote: Note): Boolean { fun hasBoosted(baseNote: Note): Boolean {
return account.hasBoosted(baseNote) return account.hasBoosted(baseNote)
} }
fun deleteBoostsTo(note: Note, signEvent: Boolean): DeletionEvent? { fun deleteBoostsTo(note: Note) {
return account.delete(account.boostsTo(note), signEvent) account.delete(account.boostsTo(note))
} }
fun calculateIfNoteWasZappedByAccount(zappedNote: Note, onWasZapped: (Boolean) -> Unit) { fun calculateIfNoteWasZappedByAccount(zappedNote: Note, onWasZapped: (Boolean) -> Unit) {
@@ -302,8 +297,8 @@ class AccountViewModel(val account: Account) : ViewModel() {
} }
} }
fun boost(note: Note, signEvent: Boolean): Event? { fun boost(note: Note) {
return account.boost(note, signEvent) account.boost(note)
} }
fun removeEmojiPack(usersEmojiList: Note, emojiList: Note) { fun removeEmojiPack(usersEmojiList: Note, emojiList: Note) {
@@ -318,24 +313,24 @@ class AccountViewModel(val account: Account) : ViewModel() {
account.addPrivateBookmark(note) account.addPrivateBookmark(note)
} }
fun addPrivateBookmark(note: Note, decryptedContent: String): BookmarkListEvent? { fun addPrivateBookmark(note: Note, decryptedContent: String) {
return account.addPrivateBookmark(note, decryptedContent) account.addPrivateBookmark(note, decryptedContent)
} }
fun addPublicBookmark(note: Note, decryptedContent: String): BookmarkListEvent? { fun addPublicBookmark(note: Note, decryptedContent: String) {
return account.addPublicBookmark(note, decryptedContent) account.addPublicBookmark(note, decryptedContent)
} }
fun removePublicBookmark(note: Note, decryptedContent: String): BookmarkListEvent? { fun removePublicBookmark(note: Note, decryptedContent: String) {
return account.removePublicBookmark(note, decryptedContent) account.removePublicBookmark(note, decryptedContent)
} }
fun addPublicBookmark(note: Note) { fun addPublicBookmark(note: Note) {
account.addPublicBookmark(note) account.addPublicBookmark(note)
} }
fun removePrivateBookmark(note: Note, decryptedContent: String): BookmarkListEvent? { fun removePrivateBookmark(note: Note, decryptedContent: String) {
return account.removePrivateBookmark(note, decryptedContent) account.removePrivateBookmark(note, decryptedContent)
} }
fun removePrivateBookmark(note: Note) { fun removePrivateBookmark(note: Note) {
@@ -358,8 +353,8 @@ class AccountViewModel(val account: Account) : ViewModel() {
account.broadcast(note) account.broadcast(note)
} }
fun delete(note: Note, signEvent: Boolean): DeletionEvent? { fun delete(note: Note) {
return account.delete(note, signEvent) account.delete(note)
} }
fun decrypt(note: Note): String? { fun decrypt(note: Note): String? {
@@ -382,12 +377,12 @@ class AccountViewModel(val account: Account) : ViewModel() {
account.prefer(source, target, preference) account.prefer(source, target, preference)
} }
fun follow(user: User, signEvent: Boolean): ContactListEvent? { fun follow(user: User) {
return account.follow(user, signEvent) account.follow(user)
} }
fun unfollow(user: User, signEvent: Boolean): ContactListEvent? { fun unfollow(user: User) {
return account.unfollow(user, signEvent) account.unfollow(user)
} }
fun isLoggedUser(user: User?): Boolean { fun isLoggedUser(user: User?): Boolean {
@@ -489,20 +484,12 @@ class AccountViewModel(val account: Account) : ViewModel() {
return account.unseal(event) return account.unseal(event)
} }
fun show(user: User, encryptedContent: String): PeopleListEvent? {
return account.showUser(user.pubkeyHex, encryptedContent)
}
fun show(user: User) { fun show(user: User) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
account.showUser(user.pubkeyHex) account.showUser(user.pubkeyHex)
} }
} }
fun hide(user: User, encryptedContent: String): PeopleListEvent? {
return account.hideUser(user.pubkeyHex, encryptedContent)
}
fun hide(user: User) { fun hide(user: User) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
account.hideUser(user.pubkeyHex) account.hideUser(user.pubkeyHex)
@@ -86,12 +86,10 @@ import com.vitorpamplona.amethyst.model.PublicChatChannel
import com.vitorpamplona.amethyst.model.ServersAvailable import com.vitorpamplona.amethyst.model.ServersAvailable
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrChannelDataSource import com.vitorpamplona.amethyst.service.NostrChannelDataSource
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.NewChannelView import com.vitorpamplona.amethyst.ui.actions.NewChannelView
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
import com.vitorpamplona.amethyst.ui.actions.PostButton import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery
import com.vitorpamplona.amethyst.ui.components.LoadNote import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
@@ -131,7 +129,6 @@ import com.vitorpamplona.amethyst.ui.theme.SmallBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.StdPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.ImmutableListOfLists
import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE
import com.vitorpamplona.quartz.events.Participant import com.vitorpamplona.quartz.events.Participant
@@ -263,24 +260,6 @@ fun ChannelScreen(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
// LAST ROW // LAST ROW
EditFieldRow(newPostModel, isPrivate = false, accountViewModel = accountViewModel) { EditFieldRow(newPostModel, isPrivate = false, accountViewModel = accountViewModel) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
@@ -292,22 +271,20 @@ fun ChannelScreen(
) )
tagger.run() tagger.run()
if (channel is PublicChatChannel) { if (channel is PublicChatChannel) {
event = accountViewModel.account.sendChannelMessage( accountViewModel.account.sendChannelMessage(
message = tagger.message, message = tagger.message,
toChannel = channel.idHex, toChannel = channel.idHex,
replyTo = tagger.replyTos, replyTo = tagger.replyTos,
mentions = tagger.mentions, mentions = tagger.mentions,
wantsToMarkAsSensitive = false, wantsToMarkAsSensitive = false
signEvent = !accountViewModel.loggedInWithAmber()
) )
} else if (channel is LiveActivitiesChannel) { } else if (channel is LiveActivitiesChannel) {
event = accountViewModel.account.sendLiveMessage( accountViewModel.account.sendLiveMessage(
message = tagger.message, message = tagger.message,
toChannel = channel.address, toChannel = channel.address,
replyTo = tagger.replyTos, replyTo = tagger.replyTos,
mentions = tagger.mentions, mentions = tagger.mentions,
wantsToMarkAsSensitive = false, wantsToMarkAsSensitive = false
signEvent = !accountViewModel.loggedInWithAmber()
) )
} }
newPostModel.message = TextFieldValue("") newPostModel.message = TextFieldValue("")
@@ -1127,30 +1104,12 @@ private fun EditButton(accountViewModel: AccountViewModel, channel: PublicChatCh
@Composable @Composable
fun JoinChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (String) -> Unit) { fun JoinChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (String) -> Unit) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
Button( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.account.follow(channel, !accountViewModel.loggedInWithAmber()) accountViewModel.account.follow(channel)
} }
}, },
shape = ButtonBorder, shape = ButtonBorder,
@@ -1167,30 +1126,12 @@ fun JoinChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (S
@Composable @Composable
fun LeaveChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (String) -> Unit) { fun LeaveChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (String) -> Unit) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
Button( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.account.unfollow(channel, !accountViewModel.loggedInWithAmber()) accountViewModel.account.unfollow(channel)
} }
}, },
shape = ButtonBorder, shape = ButtonBorder,
@@ -1207,30 +1148,12 @@ fun LeaveChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (
@Composable @Composable
fun JoinCommunityButton(accountViewModel: AccountViewModel, note: AddressableNote, nav: (String) -> Unit) { fun JoinCommunityButton(accountViewModel: AccountViewModel, note: AddressableNote, nav: (String) -> Unit) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
Button( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.account.follow(note, !accountViewModel.loggedInWithAmber()) accountViewModel.account.follow(note)
} }
}, },
shape = ButtonBorder, shape = ButtonBorder,
@@ -1247,30 +1170,12 @@ fun JoinCommunityButton(accountViewModel: AccountViewModel, note: AddressableNot
@Composable @Composable
fun LeaveCommunityButton(accountViewModel: AccountViewModel, note: AddressableNote, nav: (String) -> Unit) { fun LeaveCommunityButton(accountViewModel: AccountViewModel, note: AddressableNote, nav: (String) -> Unit) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
Button( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
event = accountViewModel.account.unfollow(note, !accountViewModel.loggedInWithAmber()) accountViewModel.account.unfollow(note)
} }
}, },
shape = ButtonBorder, shape = ButtonBorder,
@@ -63,17 +63,13 @@ import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map import androidx.lifecycle.map
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.ServersAvailable import com.vitorpamplona.amethyst.model.ServersAvailable
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.CloseButton import com.vitorpamplona.amethyst.ui.actions.CloseButton
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
import com.vitorpamplona.amethyst.ui.actions.PostButton import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
@@ -96,7 +92,6 @@ import com.vitorpamplona.amethyst.ui.theme.StdPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.ChatMessageEvent import com.vitorpamplona.quartz.events.ChatMessageEvent
import com.vitorpamplona.quartz.events.ChatroomKey import com.vitorpamplona.quartz.events.ChatroomKey
import com.vitorpamplona.quartz.events.Event
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -297,49 +292,6 @@ fun ChatroomScreen(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
var message by remember { mutableStateOf<String?>(null) }
if (message != null) {
SignerDialog(
onClose = {
message = null
},
onPost = {
scope.launch(Dispatchers.IO) {
event = accountViewModel.account.sendPrivateMessage(
message = it,
toUser = room.users.first(),
replyingTo = replyTo.value,
mentions = null,
wantsToMarkAsSensitive = false,
signEvent = false
)
message = null
}
},
data = message!!,
pubKey = room.users.first(),
type = SignerType.NIP04_ENCRYPT
)
}
// LAST ROW // LAST ROW
PrivateMessageEditFieldRow(newPostModel, isPrivate = true, accountViewModel) { PrivateMessageEditFieldRow(newPostModel, isPrivate = true, accountViewModel) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
@@ -350,22 +302,16 @@ fun ChatroomScreen(
toUsers = room.users.toList(), toUsers = room.users.toList(),
replyingTo = replyTo.value, replyingTo = replyTo.value,
mentions = null, mentions = null,
wantsToMarkAsSensitive = false, wantsToMarkAsSensitive = false
signEvent = true
) )
} else { } else {
if (!accountViewModel.isWriteable() && accountViewModel.loggedInWithAmber()) { accountViewModel.account.sendPrivateMessage(
message = newPostModel.message.text message = newPostModel.message.text,
} else { toUser = room.users.first(),
accountViewModel.account.sendPrivateMessage( replyingTo = replyTo.value,
message = newPostModel.message.text, mentions = null,
toUser = room.users.first(), wantsToMarkAsSensitive = false
replyingTo = replyTo.value, )
mentions = null,
wantsToMarkAsSensitive = false,
signEvent = true
)
}
} }
newPostModel.message = TextFieldValue("") newPostModel.message = TextFieldValue("")
@@ -700,8 +646,7 @@ fun NewSubjectView(onClose: () -> Unit, accountViewModel: AccountViewModel, room
subject = groupName.value.ifBlank { null }, subject = groupName.value.ifBlank { null },
replyingTo = null, replyingTo = null,
mentions = null, mentions = null,
wantsToMarkAsSensitive = false, wantsToMarkAsSensitive = false
signEvent = true
) )
} }
@@ -31,15 +31,11 @@ import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.fonfon.kgeohash.toGeoHash import com.fonfon.kgeohash.toGeoHash
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.NostrGeohashDataSource import com.vitorpamplona.amethyst.service.NostrGeohashDataSource
import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.screen.NostrGeoHashFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrGeoHashFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView
import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.StdPadding
import com.vitorpamplona.quartz.events.Event
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -171,29 +167,14 @@ fun GeoHashActionOptions(
userState?.user?.isFollowingGeohashCached(tag) ?: false userState?.user?.isFollowingGeohashCached(tag) ?: false
} }
} }
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
if (isFollowingTag) { if (isFollowingTag) {
UnfollowButton { UnfollowButton {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.unfollowGeohash(tag, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.unfollowGeohash(tag)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -207,7 +188,7 @@ fun GeoHashActionOptions(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.account.unfollowGeohash(tag, true) accountViewModel.account.unfollowGeohash(tag)
} }
} }
} }
@@ -215,7 +196,9 @@ fun GeoHashActionOptions(
FollowButton { FollowButton {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.followGeohash(tag, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.followGeohash(tag)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -229,7 +212,7 @@ fun GeoHashActionOptions(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.account.followGeohash(tag, true) accountViewModel.account.followGeohash(tag)
} }
} }
} }
@@ -16,10 +16,8 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -30,14 +28,10 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.NostrHashtagDataSource import com.vitorpamplona.amethyst.service.NostrHashtagDataSource
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.screen.NostrHashtagFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrHashtagFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView
import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.StdPadding
import com.vitorpamplona.quartz.events.Event
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -149,30 +143,14 @@ fun HashtagActionOptions(
userState?.user?.isFollowingHashtagCached(tag) ?: false userState?.user?.isFollowingHashtagCached(tag) ?: false
} }
} }
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
if (isFollowingTag) { if (isFollowingTag) {
UnfollowButton { UnfollowButton {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.unfollowHashtag(tag, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.unfollowHashtag(tag)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -186,7 +164,7 @@ fun HashtagActionOptions(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.account.unfollowHashtag(tag, true) accountViewModel.account.unfollowHashtag(tag)
} }
} }
} }
@@ -194,7 +172,9 @@ fun HashtagActionOptions(
FollowButton { FollowButton {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.followHashtag(tag, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.followHashtag(tag)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -208,7 +188,7 @@ fun HashtagActionOptions(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.account.followHashtag(tag, true) accountViewModel.account.followHashtag(tag)
} }
} }
} }
@@ -1,11 +1,8 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn package com.vitorpamplona.amethyst.ui.screen.loggedIn
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.* import androidx.compose.foundation.*
@@ -56,17 +53,12 @@ import androidx.lifecycle.map
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataView import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataView
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.DisplayNip05ProfileStatus import com.vitorpamplona.amethyst.ui.components.DisplayNip05ProfileStatus
import com.vitorpamplona.amethyst.ui.components.InvoiceRequestCard import com.vitorpamplona.amethyst.ui.components.InvoiceRequestCard
@@ -101,13 +93,10 @@ import com.vitorpamplona.amethyst.ui.theme.Size16Modifier
import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.AppDefinitionEvent import com.vitorpamplona.quartz.events.AppDefinitionEvent
import com.vitorpamplona.quartz.events.BadgeDefinitionEvent import com.vitorpamplona.quartz.events.BadgeDefinitionEvent
import com.vitorpamplona.quartz.events.BadgeProfilesEvent import com.vitorpamplona.quartz.events.BadgeProfilesEvent
import com.vitorpamplona.quartz.events.ChatroomKey import com.vitorpamplona.quartz.events.ChatroomKey
import com.vitorpamplona.quartz.events.ContactListEvent
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.GitHubIdentity import com.vitorpamplona.quartz.events.GitHubIdentity
import com.vitorpamplona.quartz.events.IdentityClaim import com.vitorpamplona.quartz.events.IdentityClaim
import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.ImmutableListOfLists
@@ -728,113 +717,11 @@ private fun ProfileActions(
} }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
accountViewModel.account.live.invalidateData()
accountViewModel.account.saveable.invalidateData()
event = null
}
},
data = event!!.toJson()
)
}
val encryptResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch {
Toast.makeText(
context,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@rememberLauncherForActivityResult
}
val encryptedContent = it.data?.getStringExtra("signature") ?: ""
event = accountViewModel.show(baseUser, encryptedContent)
}
)
val decryptResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch {
Toast.makeText(
context,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@rememberLauncherForActivityResult
}
val decryptedContent = it.data?.getStringExtra("signature") ?: ""
val blockList = accountViewModel.account.getBlockList()
val privateTags = if (blockList == null) {
listOf(listOf("p", baseUser.pubkeyHex))
} else {
if (accountViewModel.account.isHidden(baseUser)) {
blockList.privateTagsOrEmpty(decryptedContent).filter { element -> !element.contains(baseUser.pubkeyHex) }
} else {
blockList.privateTagsOrEmpty(decryptedContent).plus(element = listOf("p", baseUser.pubkeyHex))
}
}
val msg = Event.mapper.writeValueAsString(privateTags)
ServiceManager.shouldPauseService = true
AmberUtils.openAmber(
msg,
SignerType.NIP04_ENCRYPT,
encryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
}
)
WatchIsHiddenUser(baseUser, accountViewModel) { isHidden -> WatchIsHiddenUser(baseUser, accountViewModel) { isHidden ->
if (isHidden) { if (isHidden) {
ShowUserButton { ShowUserButton {
if (accountViewModel.loggedInWithAmber()) { accountViewModel.showUser(baseUser.pubkeyHex)
scope.launch(Dispatchers.IO) {
val blockList = accountViewModel.account.getBlockList()
val content = blockList?.content ?: ""
if (content.isBlank()) {
val privateTags = listOf(listOf("p", baseUser.pubkeyHex))
val msg = Event.mapper.writeValueAsString(privateTags)
AmberUtils.openAmber(
msg,
SignerType.NIP04_ENCRYPT,
encryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
} else {
AmberUtils.openAmber(
content,
SignerType.NIP04_DECRYPT,
decryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
}
}
} else {
accountViewModel.showUser(baseUser.pubkeyHex)
}
} }
} else { } else {
DisplayFollowUnfollowButton(baseUser, accountViewModel) DisplayFollowUnfollowButton(baseUser, accountViewModel)
@@ -858,30 +745,13 @@ private fun DisplayFollowUnfollowButton(
it.user.isFollowing(accountViewModel.account.userProfile()) it.user.isFollowing(accountViewModel.account.userProfile())
}.distinctUntilChanged().observeAsState(initial = baseUser.isFollowing(accountViewModel.account.userProfile())) }.distinctUntilChanged().observeAsState(initial = baseUser.isFollowing(accountViewModel.account.userProfile()))
var event by remember { mutableStateOf<ContactListEvent?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
event = null
}
},
data = event!!.toJson()
)
}
if (isLoggedInFollowingUser) { if (isLoggedInFollowingUser) {
UnfollowButton { UnfollowButton {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.unfollow(baseUser, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.unfollow(baseUser)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -895,7 +765,7 @@ private fun DisplayFollowUnfollowButton(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.account.unfollow(baseUser, true) accountViewModel.account.unfollow(baseUser)
} }
} }
} }
@@ -904,7 +774,9 @@ private fun DisplayFollowUnfollowButton(
FollowButton(R.string.follow_back) { FollowButton(R.string.follow_back) {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.follow(baseUser, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.follow(baseUser)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -918,7 +790,7 @@ private fun DisplayFollowUnfollowButton(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.account.follow(baseUser, true) accountViewModel.account.follow(baseUser)
} }
} }
} }
@@ -926,7 +798,9 @@ private fun DisplayFollowUnfollowButton(
FollowButton(R.string.follow) { FollowButton(R.string.follow) {
if (!accountViewModel.isWriteable()) { if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
event = accountViewModel.account.follow(baseUser, false) scope.launch(Dispatchers.IO) {
accountViewModel.account.follow(baseUser)
}
} else { } else {
scope.launch { scope.launch {
Toast Toast
@@ -940,7 +814,7 @@ private fun DisplayFollowUnfollowButton(
} }
} else { } else {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.account.follow(baseUser, true) accountViewModel.account.follow(baseUser)
} }
} }
} }
@@ -1828,88 +1702,6 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () ->
) { ) {
val clipboardManager = LocalClipboardManager.current val clipboardManager = LocalClipboardManager.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(null) }
if (event != null) {
SignerDialog(
onClose = {
event = null
},
onPost = {
scope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(it)
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
accountViewModel.account.live.invalidateData()
accountViewModel.account.saveable.invalidateData()
event = null
onDismiss()
}
},
data = event!!.toJson()
)
}
val encryptResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch {
Toast.makeText(
context,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@rememberLauncherForActivityResult
}
val encryptedContent = it.data?.getStringExtra("signature") ?: ""
event = if (accountViewModel.account.isHidden(user)) {
accountViewModel.show(user, encryptedContent)
} else {
accountViewModel.hide(user, encryptedContent)
}
}
)
val decryptResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch {
Toast.makeText(
context,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
return@rememberLauncherForActivityResult
}
val decryptedContent = it.data?.getStringExtra("signature") ?: ""
val blockList = accountViewModel.account.getBlockList()
val privateTags = if (blockList == null) {
listOf(listOf("p", user.pubkeyHex))
} else {
if (accountViewModel.account.isHidden(user)) {
blockList.privateTagsOrEmpty(decryptedContent).filter { element -> !element.contains(user.pubkeyHex) }
} else {
blockList.privateTagsOrEmpty(decryptedContent).plus(element = listOf("p", user.pubkeyHex))
}
}
val msg = Event.mapper.writeValueAsString(privateTags)
ServiceManager.shouldPauseService = true
AmberUtils.openAmber(
msg,
SignerType.NIP04_ENCRYPT,
encryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
}
)
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); onDismiss() }) { DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); onDismiss() }) {
Text(stringResource(R.string.copy_user_id)) Text(stringResource(R.string.copy_user_id))
@@ -1920,33 +1712,8 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () ->
if (accountViewModel.account.isHidden(user)) { if (accountViewModel.account.isHidden(user)) {
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
if (accountViewModel.loggedInWithAmber()) { accountViewModel.show(user)
scope.launch(Dispatchers.IO) { onDismiss()
val blockList = accountViewModel.account.getBlockList()
val content = blockList?.content ?: ""
if (content.isBlank()) {
val privateTags = listOf(listOf("p", user.pubkeyHex))
val msg = Event.mapper.writeValueAsString(privateTags)
AmberUtils.openAmber(
msg,
SignerType.NIP04_ENCRYPT,
encryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
} else {
AmberUtils.openAmber(
content,
SignerType.NIP04_DECRYPT,
decryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
}
}
} else {
accountViewModel.show(user)
onDismiss()
}
} }
) { ) {
Text(stringResource(R.string.unblock_user)) Text(stringResource(R.string.unblock_user))
@@ -1954,33 +1721,8 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () ->
} else { } else {
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
if (accountViewModel.loggedInWithAmber()) { accountViewModel.hide(user)
scope.launch(Dispatchers.IO) { onDismiss()
val blockList = accountViewModel.account.getBlockList()
val content = blockList?.content ?: ""
if (content.isBlank()) {
val privateTags = listOf(listOf("p", user.pubkeyHex))
val msg = Event.mapper.writeValueAsString(privateTags)
AmberUtils.openAmber(
msg,
SignerType.NIP04_ENCRYPT,
encryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
} else {
AmberUtils.openAmber(
content,
SignerType.NIP04_DECRYPT,
decryptResult,
accountViewModel.account.keyPair.pubKey.toHexKey()
)
}
}
} else {
accountViewModel.hide(user)
onDismiss()
}
} }
) { ) {
Text(stringResource(id = R.string.block_hide_user)) Text(stringResource(id = R.string.block_hide_user))
@@ -36,15 +36,16 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.service.PackageUtils import com.vitorpamplona.amethyst.service.PackageUtils
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog
import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.* import java.util.*
@OptIn(ExperimentalComposeUiApi::class) @OptIn(ExperimentalComposeUiApi::class)
@@ -65,7 +66,7 @@ fun LoginPage(
val useProxy = remember { mutableStateOf(false) } val useProxy = remember { mutableStateOf(false) }
val proxyPort = remember { mutableStateOf("9050") } val proxyPort = remember { mutableStateOf("9050") }
var connectOrbotDialogOpen by remember { mutableStateOf(false) } var connectOrbotDialogOpen by remember { mutableStateOf(false) }
var loginWithAmber by remember { mutableStateOf(false) } val scope = rememberCoroutineScope()
Column( Column(
modifier = Modifier modifier = Modifier
@@ -258,37 +259,6 @@ fun LoginPage(
} }
} }
if (loginWithAmber) {
SignerDialog(
onClose = {
loginWithAmber = false
},
onPost = {
key.value = TextFieldValue(it)
if (!acceptedTerms.value) {
termsAcceptanceIsRequired =
context.getString(R.string.acceptance_of_terms_is_required)
}
if (key.value.text.isBlank()) {
errorMessage = context.getString(R.string.key_is_required)
}
if (acceptedTerms.value && key.value.text.isNotBlank()) {
try {
accountViewModel.startUI(key.value.text, useProxy.value, proxyPort.value.toInt(), true)
} catch (e: Exception) {
Log.e("Login", "Could not sign in", e)
errorMessage = context.getString(R.string.invalid_key)
}
}
loginWithAmber = false
},
data = "",
type = SignerType.GET_PUBLIC_KEY
)
}
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
Box(modifier = Modifier.padding(40.dp, 0.dp, 40.dp, 0.dp)) { Box(modifier = Modifier.padding(40.dp, 0.dp, 40.dp, 0.dp)) {
@@ -329,7 +299,27 @@ fun LoginPage(
Box(modifier = Modifier.padding(40.dp, 40.dp, 40.dp, 0.dp)) { Box(modifier = Modifier.padding(40.dp, 40.dp, 40.dp, 0.dp)) {
Button( Button(
onClick = { onClick = {
loginWithAmber = true scope.launch(Dispatchers.IO) {
AmberUtils.loginWithAmber()
key.value = TextFieldValue(AmberUtils.content)
if (!acceptedTerms.value) {
termsAcceptanceIsRequired =
context.getString(R.string.acceptance_of_terms_is_required)
}
if (key.value.text.isBlank()) {
errorMessage = context.getString(R.string.key_is_required)
}
if (acceptedTerms.value && key.value.text.isNotBlank()) {
try {
accountViewModel.startUI(key.value.text, useProxy.value, proxyPort.value.toInt(), true)
} catch (e: Exception) {
Log.e("Login", "Could not sign in", e)
errorMessage = context.getString(R.string.invalid_key)
}
}
}
}, },
shape = RoundedCornerShape(Size35dp), shape = RoundedCornerShape(Size35dp),
modifier = Modifier modifier = Modifier
@@ -81,5 +81,11 @@ class BookmarkListEvent(
val sig = CryptoUtils.sign(id, privateKey) val sig = CryptoUtils.sign(id, privateKey)
return BookmarkListEvent(id.toHexKey(), pubKey.toHexKey(), createdAt, tags, content, sig.toHexKey()) return BookmarkListEvent(id.toHexKey(), pubKey.toHexKey(), createdAt, tags, content, sig.toHexKey())
} }
fun create(
unsignedEvent: BookmarkListEvent, signature: String
): BookmarkListEvent {
return BookmarkListEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -2,10 +2,12 @@ package com.vitorpamplona.quartz.events
import android.util.Log import android.util.Log
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.key
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@Immutable @Immutable
@@ -27,7 +29,7 @@ class ChannelCreateEvent(
companion object { companion object {
const val kind = 40 const val kind = 40
fun create(channelInfo: ChannelData?, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ChannelCreateEvent { fun create(channelInfo: ChannelData?, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ChannelCreateEvent {
val content = try { val content = try {
if (channelInfo != null) { if (channelInfo != null) {
mapper.writeValueAsString(channelInfo) mapper.writeValueAsString(channelInfo)
@@ -39,11 +41,15 @@ class ChannelCreateEvent(
"" ""
} }
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() val pubKey = keyPair.pubKey.toHexKey()
val tags = emptyList<List<String>>() val tags = emptyList<List<String>>()
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.pubKey)
return ChannelCreateEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) return ChannelCreateEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: ChannelCreateEvent, signature: String): ChannelCreateEvent {
return ChannelCreateEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
} }
} }
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@Immutable @Immutable
@@ -33,8 +34,7 @@ class ChannelMessageEvent(
replyTos: List<String>? = null, replyTos: List<String>? = null,
mentions: List<String>? = null, mentions: List<String>? = null,
zapReceiver: String?, zapReceiver: String?,
pubKey: HexKey, keyPair: KeyPair,
privateKey: ByteArray?,
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),
markAsSensitive: Boolean, markAsSensitive: Boolean,
zapRaiserAmount: Long?, zapRaiserAmount: Long?,
@@ -63,10 +63,17 @@ class ChannelMessageEvent(
tags.add(listOf("g", it)) tags.add(listOf("g", it))
} }
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ChannelMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "") return ChannelMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(
unsignedEvent: ChannelMessageEvent, signature: String
): ChannelMessageEvent {
return ChannelMessageEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -5,6 +5,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@Immutable @Immutable
@@ -29,7 +30,7 @@ class ChannelMetadataEvent(
companion object { companion object {
const val kind = 41 const val kind = 41
fun create(newChannelInfo: ChannelCreateEvent.ChannelData?, originalChannelIdHex: String, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ChannelMetadataEvent { fun create(newChannelInfo: ChannelCreateEvent.ChannelData?, originalChannelIdHex: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ChannelMetadataEvent {
val content = val content =
if (newChannelInfo != null) { if (newChannelInfo != null) {
mapper.writeValueAsString(newChannelInfo) mapper.writeValueAsString(newChannelInfo)
@@ -37,11 +38,15 @@ class ChannelMetadataEvent(
"" ""
} }
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() val pubKey = keyPair.pubKey.toHexKey()
val tags = listOf(listOf("e", originalChannelIdHex, "", "root")) val tags = listOf(listOf("e", originalChannelIdHex, "", "root"))
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ChannelMetadataEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) return ChannelMetadataEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: ChannelMetadataEvent, signature: String): ChannelMetadataEvent {
return ChannelMetadataEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
} }
} }
} }
@@ -261,6 +261,20 @@ class ContactListEvent(
) )
} }
fun create(
unsignedEvent: Event,
signature: String
): ContactListEvent {
return ContactListEvent(
unsignedEvent.id,
unsignedEvent.pubKey,
unsignedEvent.createdAt,
unsignedEvent.tags,
unsignedEvent.content,
signature
)
}
fun create( fun create(
content: String, content: String,
tags: List<List<String>>, tags: List<List<String>>,
@@ -29,5 +29,9 @@ class DeletionEvent(
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "") return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(unsignedEvent: DeletionEvent, signature: String): DeletionEvent {
return DeletionEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@Immutable @Immutable
@@ -28,12 +29,13 @@ class GenericRepostEvent(
companion object { companion object {
const val kind = 16 const val kind = 16
fun create(boostedPost: EventInterface, pubKey: HexKey, createdAt: Long = TimeUtils.now()): GenericRepostEvent { fun create(boostedPost: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): GenericRepostEvent {
val content = boostedPost.toJson() val content = boostedPost.toJson()
val replyToPost = listOf("e", boostedPost.id()) val replyToPost = listOf("e", boostedPost.id())
val replyToAuthor = listOf("p", boostedPost.pubKey()) val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = keyPair.pubKey.toHexKey()
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor) var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) { if (boostedPost is AddressableEvent) {
@@ -43,27 +45,12 @@ class GenericRepostEvent(
tags = tags + listOf(listOf("k", "${boostedPost.kind()}")) tags = tags + listOf(listOf("k", "${boostedPost.kind()}"))
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
return GenericRepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return GenericRepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(boostedPost: EventInterface, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): GenericRepostEvent { fun create(unsignedEvent: GenericRepostEvent, signature: String): GenericRepostEvent {
val content = boostedPost.toJson() return GenericRepostEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
val replyToPost = listOf("e", boostedPost.id())
val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) {
tags = tags + listOf(listOf("a", boostedPost.address().toTag()))
}
tags = tags + listOf(listOf("k", "${boostedPost.kind()}"))
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return GenericRepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
} }
} }
} }
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@Immutable @Immutable
@@ -23,7 +24,7 @@ class HTTPAuthorizationEvent(
url: String, url: String,
method: String, method: String,
body: String? = null, body: String? = null,
privateKey: ByteArray, keyPair: KeyPair,
createdAt: Long = TimeUtils.now() createdAt: Long = TimeUtils.now()
): HTTPAuthorizationEvent { ): HTTPAuthorizationEvent {
var hash = "" var hash = ""
@@ -37,10 +38,16 @@ class HTTPAuthorizationEvent(
listOf("payload", hash) listOf("payload", hash)
) )
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "") val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return HTTPAuthorizationEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey()) return HTTPAuthorizationEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: HTTPAuthorizationEvent, signature: String
): HTTPAuthorizationEvent {
return HTTPAuthorizationEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
} }
} }
} }
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@@ -49,8 +50,7 @@ class LiveActivitiesChatMessageEvent(
replyTos: List<String>? = null, replyTos: List<String>? = null,
mentions: List<String>? = null, mentions: List<String>? = null,
zapReceiver: String?, zapReceiver: String?,
pubKey: HexKey, keyPair: KeyPair,
privateKey: ByteArray?,
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),
markAsSensitive: Boolean, markAsSensitive: Boolean,
zapRaiserAmount: Long?, zapRaiserAmount: Long?,
@@ -79,9 +79,16 @@ class LiveActivitiesChatMessageEvent(
tags.add(listOf("g", it)) tags.add(listOf("g", it))
} }
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return LiveActivitiesChatMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "") return LiveActivitiesChatMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(
unsignedEvent: LiveActivitiesChatMessageEvent, signature: String
): LiveActivitiesChatMessageEvent {
return LiveActivitiesChatMessageEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -160,5 +160,9 @@ class MetadataEvent(
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey) val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey)
return MetadataEvent(id.toHexKey(), pubKey, createdAt, tags, contactMetaData, sig?.toHexKey() ?: "") return MetadataEvent(id.toHexKey(), pubKey, createdAt, tags, contactMetaData, sig?.toHexKey() ?: "")
} }
fun create(unsignedEvent: MetadataEvent, signature: String): MetadataEvent {
return MetadataEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -228,5 +228,9 @@ class PeopleListEvent(
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
return PeopleListEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") return PeopleListEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
} }
fun create(unsignedEvent: PeopleListEvent, signature: String): PeopleListEvent {
return PeopleListEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -93,6 +93,12 @@ class PollNoteEvent(
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey) val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey)
return PollNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "") return PollNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "")
} }
fun create(
unsignedEvent: PollNoteEvent, signature: String
): PollNoteEvent {
return PollNoteEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -5,6 +5,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.HexValidator import com.vitorpamplona.quartz.encoders.HexValidator
import com.vitorpamplona.quartz.encoders.Hex import com.vitorpamplona.quartz.encoders.Hex
@@ -86,8 +87,7 @@ class PrivateDmEvent(
replyTos: List<String>? = null, replyTos: List<String>? = null,
mentions: List<String>? = null, mentions: List<String>? = null,
zapReceiver: String?, zapReceiver: String?,
pubKey: HexKey, keyPair: KeyPair,
privateKey: ByteArray?,
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),
publishedRecipientPubKey: ByteArray? = null, publishedRecipientPubKey: ByteArray? = null,
advertiseNip18: Boolean = true, advertiseNip18: Boolean = true,
@@ -96,9 +96,9 @@ class PrivateDmEvent(
geohash: String? = null geohash: String? = null
): PrivateDmEvent { ): PrivateDmEvent {
val message = if (advertiseNip18) { nip18Advertisement } else { "" } + msg val message = if (advertiseNip18) { nip18Advertisement } else { "" } + msg
val content = if (privateKey == null) message else CryptoUtils.encryptNIP04( val content = if (keyPair.privKey == null) message else CryptoUtils.encryptNIP04(
message, message,
privateKey, keyPair.privKey,
recipientPubKey recipientPubKey
) )
val tags = mutableListOf<List<String>>() val tags = mutableListOf<List<String>>()
@@ -124,9 +124,17 @@ class PrivateDmEvent(
tags.add(listOf("g", it)) tags.add(listOf("g", it))
} }
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return PrivateDmEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "") return PrivateDmEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(
unsignedEvent: PrivateDmEvent,
signature: String,
): PrivateDmEvent {
return PrivateDmEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -61,5 +61,9 @@ class ReactionEvent(
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "") return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(unsignedEvent: ReactionEvent, signature: String): ReactionEvent {
return ReactionEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -32,5 +32,9 @@ class RelayAuthEvent(
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey) val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey)
return RelayAuthEvent(id.toHexKey(), localPubKey, createdAt, tags, content, sig?.toHexKey() ?: "") return RelayAuthEvent(id.toHexKey(), localPubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(unsignedEvent: RelayAuthEvent, signature: String): RelayAuthEvent {
return RelayAuthEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@Immutable @Immutable
@@ -28,12 +29,13 @@ class RepostEvent(
companion object { companion object {
const val kind = 6 const val kind = 6
fun create(boostedPost: EventInterface, pubKey: HexKey, createdAt: Long = TimeUtils.now()): RepostEvent { fun create(boostedPost: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): RepostEvent {
val content = boostedPost.toJson() val content = boostedPost.toJson()
val replyToPost = listOf("e", boostedPost.id()) val replyToPost = listOf("e", boostedPost.id())
val replyToAuthor = listOf("p", boostedPost.pubKey()) val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = keyPair.pubKey.toHexKey()
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor) var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) { if (boostedPost is AddressableEvent) {
@@ -41,25 +43,12 @@ class RepostEvent(
} }
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
return RepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return RepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
} }
fun create(boostedPost: EventInterface, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): RepostEvent { fun create(unsignedEvent: RepostEvent, signature: String): RepostEvent {
val content = boostedPost.toJson() return RepostEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
val replyToPost = listOf("e", boostedPost.id())
val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) {
tags = tags + listOf(listOf("a", boostedPost.address().toTag()))
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return RepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
} }
} }
} }
@@ -100,6 +100,13 @@ class TextNoteEvent(
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey) val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return TextNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "") return TextNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "")
} }
fun create(
unsignedEvent: TextNoteEvent, signature: String
): TextNoteEvent {
return TextNoteEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
} }
} }