mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-04-08 20:08:06 +02:00
add/remove private bookmarks
This commit is contained in:
parent
46571a6029
commit
e817f94045
@ -8,6 +8,7 @@ import androidx.core.os.ConfigurationCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import com.vitorpamplona.amethyst.OptOutFromFilters
|
||||
import com.vitorpamplona.amethyst.service.AmberUtils
|
||||
import com.vitorpamplona.amethyst.service.FileHeader
|
||||
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
|
||||
import com.vitorpamplona.amethyst.service.relays.Client
|
||||
@ -1237,6 +1238,96 @@ class Account(
|
||||
LocalCache.consume(event)
|
||||
}
|
||||
|
||||
fun addPrivateBookmark(note: Note, decryptedContent: String): BookmarkListEvent? {
|
||||
val bookmarks = userProfile().latestBookmarkList
|
||||
val privTags = mutableListOf<List<String>>()
|
||||
|
||||
val privEvents = if (note is AddressableNote) {
|
||||
bookmarks?.privateTaggedEvents(decryptedContent) ?: emptyList()
|
||||
} else {
|
||||
bookmarks?.privateTaggedEvents(decryptedContent)?.plus(note.idHex) ?: listOf(note.idHex)
|
||||
}
|
||||
val privUsers = bookmarks?.privateTaggedUsers(decryptedContent) ?: emptyList()
|
||||
val privAddresses = if (note is AddressableNote) {
|
||||
bookmarks?.privateTaggedAddresses(decryptedContent)?.plus(note.address) ?: listOf(note.address)
|
||||
} else {
|
||||
bookmarks?.privateTaggedAddresses(decryptedContent) ?: emptyList()
|
||||
}
|
||||
|
||||
privEvents.forEach {
|
||||
privTags.add(listOf("e", it))
|
||||
}
|
||||
privUsers.forEach {
|
||||
privTags.add(listOf("p", it))
|
||||
}
|
||||
privAddresses.forEach {
|
||||
privTags.add(listOf("a", it.toTag()))
|
||||
}
|
||||
val msg = Event.mapper.writeValueAsString(privTags)
|
||||
|
||||
AmberUtils.encryptBookmark(msg, keyPair.pubKey.toHexKey())
|
||||
|
||||
if (AmberUtils.content.isBlank()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return BookmarkListEvent.create(
|
||||
"bookmark",
|
||||
bookmarks?.taggedEvents() ?: emptyList(),
|
||||
bookmarks?.taggedUsers() ?: emptyList(),
|
||||
bookmarks?.taggedAddresses() ?: emptyList(),
|
||||
|
||||
AmberUtils.content,
|
||||
|
||||
keyPair.pubKey.toHexKey()
|
||||
)
|
||||
}
|
||||
|
||||
fun removePrivateBookmark(note: Note, decryptedContent: String): BookmarkListEvent? {
|
||||
val bookmarks = userProfile().latestBookmarkList
|
||||
val privTags = mutableListOf<List<String>>()
|
||||
|
||||
val privEvents = if (note is AddressableNote) {
|
||||
bookmarks?.privateTaggedEvents(decryptedContent) ?: emptyList()
|
||||
} else {
|
||||
bookmarks?.privateTaggedEvents(decryptedContent)?.minus(note.idHex) ?: listOf(note.idHex)
|
||||
}
|
||||
val privUsers = bookmarks?.privateTaggedUsers(decryptedContent) ?: emptyList()
|
||||
val privAddresses = if (note is AddressableNote) {
|
||||
bookmarks?.privateTaggedAddresses(decryptedContent)?.minus(note.address) ?: listOf(note.address)
|
||||
} else {
|
||||
bookmarks?.privateTaggedAddresses(decryptedContent) ?: emptyList()
|
||||
}
|
||||
|
||||
privEvents.forEach {
|
||||
privTags.add(listOf("e", it))
|
||||
}
|
||||
privUsers.forEach {
|
||||
privTags.add(listOf("p", it))
|
||||
}
|
||||
privAddresses.forEach {
|
||||
privTags.add(listOf("a", it.toTag()))
|
||||
}
|
||||
val msg = Event.mapper.writeValueAsString(privTags)
|
||||
|
||||
AmberUtils.encryptBookmark(msg, keyPair.pubKey.toHexKey())
|
||||
|
||||
if (AmberUtils.content.isBlank()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return BookmarkListEvent.create(
|
||||
"bookmark",
|
||||
bookmarks?.taggedEvents() ?: emptyList(),
|
||||
bookmarks?.taggedUsers() ?: emptyList(),
|
||||
bookmarks?.taggedAddresses() ?: emptyList(),
|
||||
|
||||
AmberUtils.content,
|
||||
|
||||
keyPair.pubKey.toHexKey()
|
||||
)
|
||||
}
|
||||
|
||||
fun addPrivateBookmark(note: Note) {
|
||||
if (!isWriteable()) return
|
||||
|
||||
@ -1392,14 +1483,24 @@ class Account(
|
||||
}
|
||||
|
||||
fun isInPrivateBookmarks(note: Note): Boolean {
|
||||
if (!isWriteable()) return false
|
||||
if (!isWriteable() && !loginWithAmber) return false
|
||||
|
||||
if (note is AddressableNote) {
|
||||
return userProfile().latestBookmarkList?.privateTaggedAddresses(keyPair.privKey!!)
|
||||
?.contains(note.address) == true
|
||||
if (loginWithAmber) {
|
||||
return if (note is AddressableNote) {
|
||||
userProfile().latestBookmarkList?.privateTaggedAddresses(userProfile().latestBookmarkList?.decryptedContent ?: "")
|
||||
?.contains(note.address) == true
|
||||
} else {
|
||||
userProfile().latestBookmarkList?.privateTaggedEvents(userProfile().latestBookmarkList?.decryptedContent ?: "")
|
||||
?.contains(note.idHex) == true
|
||||
}
|
||||
} else {
|
||||
return userProfile().latestBookmarkList?.privateTaggedEvents(keyPair.privKey!!)
|
||||
?.contains(note.idHex) == true
|
||||
return if (note is AddressableNote) {
|
||||
userProfile().latestBookmarkList?.privateTaggedAddresses(keyPair.privKey!!)
|
||||
?.contains(note.address) == true
|
||||
} else {
|
||||
userProfile().latestBookmarkList?.privateTaggedEvents(keyPair.privKey!!)
|
||||
?.contains(note.idHex) == true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,10 @@ import android.util.Log
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.LocalPreferences
|
||||
import com.vitorpamplona.amethyst.service.AmberUtils
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||
import com.vitorpamplona.amethyst.ui.components.BundledInsert
|
||||
import com.vitorpamplona.amethyst.ui.dal.BookmarkPrivateFeedFilter
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.Hex
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
@ -207,7 +207,7 @@ object LocalCache {
|
||||
if (hexKey != null) {
|
||||
val pubKey = Hex.encode(hexKey)
|
||||
if (pubKey == event.pubKey) {
|
||||
BookmarkPrivateFeedFilter.content = ""
|
||||
AmberUtils.content = ""
|
||||
}
|
||||
}
|
||||
user.updateBookmark(event)
|
||||
|
@ -0,0 +1,40 @@
|
||||
package com.vitorpamplona.amethyst.service
|
||||
|
||||
import com.vitorpamplona.amethyst.ui.actions.SignerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.openAmber
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
|
||||
object AmberUtils {
|
||||
var content: String = ""
|
||||
var isActivityRunning: Boolean = false
|
||||
|
||||
fun decryptBookmark(encryptedContent: String, pubKey: HexKey) {
|
||||
if (content.isBlank()) {
|
||||
isActivityRunning = true
|
||||
openAmber(
|
||||
encryptedContent,
|
||||
SignerType.NIP04_DECRYPT,
|
||||
IntentUtils.decryptActivityResultLauncher,
|
||||
pubKey
|
||||
)
|
||||
while (isActivityRunning) {
|
||||
Thread.sleep(250)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun encryptBookmark(decryptedContent: String, pubKey: HexKey) {
|
||||
if (content.isBlank()) {
|
||||
isActivityRunning = true
|
||||
openAmber(
|
||||
decryptedContent,
|
||||
SignerType.NIP04_ENCRYPT,
|
||||
IntentUtils.decryptActivityResultLauncher,
|
||||
pubKey
|
||||
)
|
||||
while (isActivityRunning) {
|
||||
Thread.sleep(250)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.LocalPreferences
|
||||
import com.vitorpamplona.amethyst.ServiceManager
|
||||
import com.vitorpamplona.amethyst.service.AmberUtils
|
||||
import com.vitorpamplona.amethyst.service.IntentUtils
|
||||
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
|
||||
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
||||
@ -32,7 +33,6 @@ import com.vitorpamplona.amethyst.service.notifications.RegisterAccounts
|
||||
import com.vitorpamplona.amethyst.service.relays.Client
|
||||
import com.vitorpamplona.amethyst.ui.components.DefaultMutedSetting
|
||||
import com.vitorpamplona.amethyst.ui.components.keepPlayingMutex
|
||||
import com.vitorpamplona.amethyst.ui.dal.BookmarkPrivateFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.debugState
|
||||
import com.vitorpamplona.amethyst.ui.note.Nip47
|
||||
@ -122,13 +122,13 @@ class MainActivity : AppCompatActivity() {
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
BookmarkPrivateFeedFilter.isActivityRunning = false
|
||||
AmberUtils.isActivityRunning = false
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val event = it.data?.getStringExtra("signature") ?: ""
|
||||
BookmarkPrivateFeedFilter.content = event
|
||||
BookmarkPrivateFeedFilter.isActivityRunning = false
|
||||
AmberUtils.content = event
|
||||
AmberUtils.isActivityRunning = false
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -3,15 +3,11 @@ package com.vitorpamplona.amethyst.ui.dal
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.IntentUtils
|
||||
import com.vitorpamplona.amethyst.ui.actions.SignerType
|
||||
import com.vitorpamplona.amethyst.ui.actions.openAmber
|
||||
import com.vitorpamplona.amethyst.service.AmberUtils
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
|
||||
object BookmarkPrivateFeedFilter : FeedFilter<Note>() {
|
||||
lateinit var account: Account
|
||||
var content: String = ""
|
||||
var isActivityRunning: Boolean = false
|
||||
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().latestBookmarkList?.id ?: ""
|
||||
@ -21,22 +17,14 @@ object BookmarkPrivateFeedFilter : FeedFilter<Note>() {
|
||||
val bookmarks = account.userProfile().latestBookmarkList
|
||||
|
||||
if (account.loginWithAmber) {
|
||||
if (content.isBlank()) {
|
||||
isActivityRunning = true
|
||||
openAmber(
|
||||
bookmarks?.content ?: "",
|
||||
SignerType.NIP04_DECRYPT,
|
||||
IntentUtils.decryptActivityResultLauncher,
|
||||
account.keyPair.pubKey.toHexKey()
|
||||
)
|
||||
while (isActivityRunning) {
|
||||
Thread.sleep(250)
|
||||
}
|
||||
if (AmberUtils.content.isBlank()) {
|
||||
AmberUtils.decryptBookmark(bookmarks?.content ?: "", account.keyPair.pubKey.toHexKey())
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
}
|
||||
|
||||
val notes = bookmarks?.privateTaggedEvents(content)
|
||||
val notes = bookmarks?.privateTaggedEvents(bookmarks.decryptedContent)
|
||||
?.mapNotNull { LocalCache.checkGetOrCreateNote(it) } ?: emptyList()
|
||||
val addresses = bookmarks?.privateTaggedAddresses(content)
|
||||
val addresses = bookmarks?.privateTaggedAddresses(bookmarks.decryptedContent)
|
||||
?.map { LocalCache.getOrCreateAddressableNote(it) } ?: emptyList()
|
||||
|
||||
return notes.plus(addresses).toSet()
|
||||
|
@ -41,13 +41,19 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.map
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
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.components.RobohashAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@ -390,6 +396,25 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
|
||||
|
||||
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) {
|
||||
DropdownMenuItem(onClick = {
|
||||
accountViewModel.follow(
|
||||
@ -447,11 +472,47 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
|
||||
}
|
||||
Divider()
|
||||
if (state.isPrivateBookmarkNote) {
|
||||
DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.removePrivateBookmark(note); onDismiss() } }) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (accountViewModel.loggedInWithAmber()) {
|
||||
val bookmarks = accountViewModel.userProfile().latestBookmarkList
|
||||
AmberUtils.decryptBookmark(
|
||||
bookmarks?.content ?: "",
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey()
|
||||
)
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
event = accountViewModel.removePrivateBookmark(note, bookmarks?.decryptedContent ?: "")
|
||||
} else {
|
||||
accountViewModel.removePrivateBookmark(note)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.remove_from_private_bookmarks))
|
||||
}
|
||||
} else {
|
||||
DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.addPrivateBookmark(note); onDismiss() } }) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (accountViewModel.loggedInWithAmber()) {
|
||||
val bookmarks = accountViewModel.userProfile().latestBookmarkList
|
||||
AmberUtils.decryptBookmark(
|
||||
bookmarks?.content ?: "",
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey()
|
||||
)
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
event = accountViewModel.addPrivateBookmark(note, bookmarks?.decryptedContent ?: "")
|
||||
} else {
|
||||
accountViewModel.addPrivateBookmark(note)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.add_to_private_bookmarks))
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.model.UserState
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.events.BookmarkListEvent
|
||||
import com.vitorpamplona.quartz.events.DeletionEvent
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import com.vitorpamplona.quartz.events.GiftWrapEvent
|
||||
@ -231,10 +232,18 @@ class AccountViewModel(val account: Account) : ViewModel() {
|
||||
account.addPrivateBookmark(note)
|
||||
}
|
||||
|
||||
fun addPrivateBookmark(note: Note, decryptedContent: String): BookmarkListEvent? {
|
||||
return account.addPrivateBookmark(note, decryptedContent)
|
||||
}
|
||||
|
||||
fun addPublicBookmark(note: Note) {
|
||||
account.addPublicBookmark(note)
|
||||
}
|
||||
|
||||
fun removePrivateBookmark(note: Note, decryptedContent: String): BookmarkListEvent? {
|
||||
return account.removePrivateBookmark(note, decryptedContent)
|
||||
}
|
||||
|
||||
fun removePrivateBookmark(note: Note) {
|
||||
account.removePrivateBookmark(note)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.AmberUtils
|
||||
import com.vitorpamplona.amethyst.ui.dal.BookmarkPrivateFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.BookmarkPublicFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.NostrBookmarkPrivateFeedViewModel
|
||||
@ -95,7 +96,7 @@ fun BookmarkListScreen(accountViewModel: AccountViewModel, nav: (String) -> Unit
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
BookmarkPrivateFeedFilter.content = ""
|
||||
AmberUtils.content = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,37 @@ class BookmarkListEvent(
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : GeneralListEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
var decryptedContent = ""
|
||||
|
||||
companion object {
|
||||
const val kind = 30001
|
||||
|
||||
fun create(
|
||||
name: String = "",
|
||||
events: List<String>? = null,
|
||||
users: List<String>? = null,
|
||||
addresses: List<ATag>? = null,
|
||||
content: String,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long = TimeUtils.now()
|
||||
): BookmarkListEvent {
|
||||
val tags = mutableListOf<List<String>>()
|
||||
tags.add(listOf("d", name))
|
||||
|
||||
events?.forEach {
|
||||
tags.add(listOf("e", it))
|
||||
}
|
||||
users?.forEach {
|
||||
tags.add(listOf("p", it))
|
||||
}
|
||||
addresses?.forEach {
|
||||
tags.add(listOf("a", it.toTag()))
|
||||
}
|
||||
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
return BookmarkListEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
|
||||
}
|
||||
|
||||
fun create(
|
||||
name: String = "",
|
||||
|
||||
|
@ -71,9 +71,11 @@ abstract class GeneralListEvent(
|
||||
}
|
||||
|
||||
fun privateTaggedUsers(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "p" }?.map { it[1] }
|
||||
|
||||
fun privateTaggedUsers(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "p" }?.map { it[1] }
|
||||
fun privateHashtags(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "t" }?.map { it[1] }
|
||||
fun privateHashtags(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "t" }?.map { it[1] }
|
||||
fun privateGeohashes(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "g" }?.map { it[1] }
|
||||
fun privateGeohashes(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "g" }?.map { it[1] }
|
||||
fun privateTaggedEvents(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "e" }?.map { it[1] }
|
||||
fun privateTaggedEvents(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "e" }?.map { it[1] }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user