mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-10-09 21:02:33 +02:00
Edit Profile
This commit is contained in:
@@ -9,11 +9,13 @@ import com.vitorpamplona.amethyst.service.model.ReactionEvent
|
|||||||
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
||||||
import com.vitorpamplona.amethyst.service.relays.Client
|
import com.vitorpamplona.amethyst.service.relays.Client
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import java.util.Date
|
||||||
import nostr.postr.Contact
|
import nostr.postr.Contact
|
||||||
import nostr.postr.Persona
|
import nostr.postr.Persona
|
||||||
import nostr.postr.Utils
|
import nostr.postr.Utils
|
||||||
import nostr.postr.events.ContactListEvent
|
import nostr.postr.events.ContactListEvent
|
||||||
|
import nostr.postr.events.Event
|
||||||
|
import nostr.postr.events.MetadataEvent
|
||||||
import nostr.postr.events.PrivateDmEvent
|
import nostr.postr.events.PrivateDmEvent
|
||||||
import nostr.postr.events.TextNoteEvent
|
import nostr.postr.events.TextNoteEvent
|
||||||
import nostr.postr.toHex
|
import nostr.postr.toHex
|
||||||
@@ -38,6 +40,23 @@ class Account(val loggedIn: Persona, val followingChannels: MutableSet<String> =
|
|||||||
return loggedIn.privKey != null
|
return loggedIn.privKey != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendNewUserMetadata(toString: String) {
|
||||||
|
if (!isWriteable()) return
|
||||||
|
|
||||||
|
loggedIn.privKey?.let {
|
||||||
|
val createdAt = Date().time / 1000
|
||||||
|
val content = toString
|
||||||
|
val pubKey = Utils.pubkeyCreate(it)
|
||||||
|
val tags = listOf<List<String>>()
|
||||||
|
val id = Event.generateId(pubKey, createdAt, MetadataEvent.kind, tags, content)
|
||||||
|
val sig = Utils.sign(id, it)
|
||||||
|
val event = MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
|
||||||
|
Client.send(event)
|
||||||
|
LocalCache.consume(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun reactTo(note: Note) {
|
fun reactTo(note: Note) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
@@ -72,7 +91,7 @@ class Account(val loggedIn: Persona, val followingChannels: MutableSet<String> =
|
|||||||
fun follow(user: User) {
|
fun follow(user: User) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
val lastestContactList = userProfile().lastestContactList
|
val lastestContactList = userProfile().latestContactList
|
||||||
val event = if (lastestContactList != null) {
|
val event = if (lastestContactList != null) {
|
||||||
ContactListEvent.create(lastestContactList.follows.plus(Contact(user.pubkeyHex, null)), lastestContactList.relayUse, loggedIn.privKey!!)
|
ContactListEvent.create(lastestContactList.follows.plus(Contact(user.pubkeyHex, null)), lastestContactList.relayUse, loggedIn.privKey!!)
|
||||||
} else {
|
} else {
|
||||||
@@ -87,7 +106,7 @@ class Account(val loggedIn: Persona, val followingChannels: MutableSet<String> =
|
|||||||
fun unfollow(user: User) {
|
fun unfollow(user: User) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
val lastestContactList = userProfile().lastestContactList
|
val lastestContactList = userProfile().latestContactList
|
||||||
if (lastestContactList != null) {
|
if (lastestContactList != null) {
|
||||||
val event = ContactListEvent.create(lastestContactList.follows.filter { it.pubKeyHex != user.pubkeyHex }, lastestContactList.relayUse, loggedIn.privKey!!)
|
val event = ContactListEvent.create(lastestContactList.follows.filter { it.pubKeyHex != user.pubkeyHex }, lastestContactList.relayUse, loggedIn.privKey!!)
|
||||||
Client.send(event)
|
Client.send(event)
|
||||||
@@ -221,6 +240,8 @@ class Account(val loggedIn: Persona, val followingChannels: MutableSet<String> =
|
|||||||
private fun refreshObservers() {
|
private fun refreshObservers() {
|
||||||
live.refresh()
|
live.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccountLiveData(private val account: Account): LiveData<AccountState>(AccountState(account)) {
|
class AccountLiveData(private val account: Account): LiveData<AccountState>(AccountState(account)) {
|
||||||
|
@@ -80,6 +80,7 @@ object LocalCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldUser.updateUserInfo(newUser, event.createdAt)
|
oldUser.updateUserInfo(newUser, event.createdAt)
|
||||||
|
oldUser.latestMetadata = event
|
||||||
} else {
|
} else {
|
||||||
//Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()} ${formattedDateTime(event.createdAt)} > ${formattedDateTime(oldUser.updatedAt)}")
|
//Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()} ${formattedDateTime(event.createdAt)} > ${formattedDateTime(oldUser.updatedAt)}")
|
||||||
}
|
}
|
||||||
@@ -147,7 +148,7 @@ object LocalCache {
|
|||||||
event.createdAt
|
event.createdAt
|
||||||
)
|
)
|
||||||
|
|
||||||
user.lastestContactList = event
|
user.latestContactList = event
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshObservers()
|
refreshObservers()
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
package com.vitorpamplona.amethyst.model
|
package com.vitorpamplona.amethyst.model
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
||||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
@@ -14,6 +11,7 @@ import kotlinx.coroutines.Job
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import nostr.postr.events.ContactListEvent
|
import nostr.postr.events.ContactListEvent
|
||||||
|
import nostr.postr.events.MetadataEvent
|
||||||
|
|
||||||
class User(val pubkey: ByteArray) {
|
class User(val pubkey: ByteArray) {
|
||||||
val pubkeyHex = pubkey.toHexKey()
|
val pubkeyHex = pubkey.toHexKey()
|
||||||
@@ -24,7 +22,8 @@ class User(val pubkey: ByteArray) {
|
|||||||
var updatedMetadataAt: Long = 0;
|
var updatedMetadataAt: Long = 0;
|
||||||
var updatedFollowsAt: Long = 0;
|
var updatedFollowsAt: Long = 0;
|
||||||
|
|
||||||
var lastestContactList: ContactListEvent? = null
|
var latestContactList: ContactListEvent? = null
|
||||||
|
var latestMetadata: MetadataEvent? = null
|
||||||
|
|
||||||
val notes = Collections.synchronizedSet(mutableSetOf<Note>())
|
val notes = Collections.synchronizedSet(mutableSetOf<Note>())
|
||||||
val follows = Collections.synchronizedSet(mutableSetOf<User>())
|
val follows = Collections.synchronizedSet(mutableSetOf<User>())
|
||||||
|
@@ -0,0 +1,211 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.OutlinedTextField
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
|
import com.vitorpamplona.amethyst.model.Channel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
|
||||||
|
val postViewModel: NewUserMetadataViewModel = viewModel()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
postViewModel.load(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = { onClose() },
|
||||||
|
properties = DialogProperties(
|
||||||
|
usePlatformDefaultWidth = false,
|
||||||
|
dismissOnClickOutside = false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
CloseButton(onCancel = {
|
||||||
|
postViewModel.clear()
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
|
||||||
|
PostButton(
|
||||||
|
onPost = {
|
||||||
|
postViewModel.create()
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
postViewModel.userName.value.isNotBlank()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
Row(modifier = Modifier.fillMaxWidth(1f), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "Display Name") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
value = postViewModel.displayName.value,
|
||||||
|
onValueChange = { postViewModel.displayName.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "My display name",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
capitalization = KeyboardCapitalization.Sentences
|
||||||
|
),
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Text("@", Modifier.padding(5.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "Username") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
value = postViewModel.userName.value,
|
||||||
|
onValueChange = { postViewModel.userName.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "My username",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "About me") },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(100.dp),
|
||||||
|
value = postViewModel.about.value,
|
||||||
|
onValueChange = { postViewModel.about.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "About me",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
capitalization = KeyboardCapitalization.Sentences
|
||||||
|
),
|
||||||
|
maxLines = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "Avatar URL") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = postViewModel.picture.value,
|
||||||
|
onValueChange = { postViewModel.picture.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "https://mywebsite.com/me.jpg",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "Banner URL") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = postViewModel.banner.value,
|
||||||
|
onValueChange = { postViewModel.banner.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "https://mywebsite.com/mybanner.jpg",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "Website URL") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = postViewModel.website.value,
|
||||||
|
onValueChange = { postViewModel.website.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "https://mywebsite.com",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "NIP-05") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = postViewModel.nip05.value,
|
||||||
|
onValueChange = { postViewModel.nip05.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "_@mywebsite.com",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = "LN Address") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = postViewModel.lnAddress.value,
|
||||||
|
onValueChange = { postViewModel.lnAddress.value = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "me@mylightiningnode.com",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,79 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||||
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.StringWriter
|
||||||
|
|
||||||
|
class NewUserMetadataViewModel: ViewModel() {
|
||||||
|
private lateinit var account: Account
|
||||||
|
|
||||||
|
val userName = mutableStateOf("")
|
||||||
|
val displayName = mutableStateOf("")
|
||||||
|
val about = mutableStateOf("")
|
||||||
|
|
||||||
|
val picture = mutableStateOf("")
|
||||||
|
val banner = mutableStateOf("")
|
||||||
|
|
||||||
|
val website = mutableStateOf("")
|
||||||
|
val nip05 = mutableStateOf("")
|
||||||
|
val lnAddress = mutableStateOf("")
|
||||||
|
|
||||||
|
fun load(account: Account) {
|
||||||
|
this.account = account
|
||||||
|
|
||||||
|
account.userProfile().let {
|
||||||
|
userName.value = it.bestUsername() ?: ""
|
||||||
|
displayName.value = it.bestDisplayName() ?: ""
|
||||||
|
about.value = it.info.about ?: ""
|
||||||
|
picture.value = it.info.picture ?: ""
|
||||||
|
banner.value = it.info.banner ?: ""
|
||||||
|
website.value = it.info.website ?: ""
|
||||||
|
nip05.value = it.info.nip05 ?: ""
|
||||||
|
lnAddress.value = it.info.lud16 ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create() {
|
||||||
|
// Tries to not delete any existing attribute that we do not work with.
|
||||||
|
val latest = account.userProfile().latestMetadata
|
||||||
|
val currentJson = if (latest != null) {
|
||||||
|
ObjectMapper().readTree(
|
||||||
|
ByteArrayInputStream(latest.content.toByteArray(Charsets.UTF_8))
|
||||||
|
) as ObjectNode
|
||||||
|
} else {
|
||||||
|
ObjectMapper().createObjectNode()
|
||||||
|
}
|
||||||
|
currentJson.put("name", userName.value)
|
||||||
|
currentJson.put("username", userName.value)
|
||||||
|
currentJson.put("display_name", displayName.value)
|
||||||
|
currentJson.put("displayName", displayName.value)
|
||||||
|
currentJson.put("picture", picture.value)
|
||||||
|
currentJson.put("banner", banner.value)
|
||||||
|
currentJson.put("website", website.value)
|
||||||
|
currentJson.put("about", about.value)
|
||||||
|
currentJson.put("nip05", nip05.value)
|
||||||
|
currentJson.put("lud06", lnAddress.value)
|
||||||
|
|
||||||
|
val writer = StringWriter()
|
||||||
|
ObjectMapper().writeValue(writer, currentJson)
|
||||||
|
|
||||||
|
account.sendNewUserMetadata(writer.buffer.toString())
|
||||||
|
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
userName.value = ""
|
||||||
|
displayName.value = ""
|
||||||
|
about.value = ""
|
||||||
|
picture.value = ""
|
||||||
|
banner.value = ""
|
||||||
|
website.value = ""
|
||||||
|
nip05.value = ""
|
||||||
|
lnAddress.value = ""
|
||||||
|
}
|
||||||
|
}
|
@@ -30,7 +30,10 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
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.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.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -51,21 +54,19 @@ import com.google.accompanist.pager.HorizontalPager
|
|||||||
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
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.model.toNote
|
import com.vitorpamplona.amethyst.model.toNote
|
||||||
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrUserProfileFollowersDataSource
|
import com.vitorpamplona.amethyst.service.NostrUserProfileFollowersDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrUserProfileFollowsDataSource
|
import com.vitorpamplona.amethyst.service.NostrUserProfileFollowsDataSource
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataView
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import nostr.postr.toNpub
|
import nostr.postr.toNpub
|
||||||
|
|
||||||
data class TabRowItem(
|
|
||||||
val title: String,
|
|
||||||
val screen: @Composable () -> Unit,
|
|
||||||
)
|
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navController: NavController) {
|
fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
@@ -75,7 +76,7 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
|
|||||||
val accountUserState by account.userProfile().live.observeAsState()
|
val accountUserState by account.userProfile().live.observeAsState()
|
||||||
val accountUser = accountUserState?.user
|
val accountUser = accountUserState?.user
|
||||||
|
|
||||||
if (userId != null && account != null && accountUser != null) {
|
if (userId != null && accountUser != null) {
|
||||||
DisposableEffect(account) {
|
DisposableEffect(account) {
|
||||||
NostrUserProfileDataSource.loadUserProfile(userId)
|
NostrUserProfileDataSource.loadUserProfile(userId)
|
||||||
NostrUserProfileFollowersDataSource.loadUserProfile(userId)
|
NostrUserProfileFollowersDataSource.loadUserProfile(userId)
|
||||||
@@ -93,6 +94,8 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
|
|||||||
val userState by baseUser.live.observeAsState()
|
val userState by baseUser.live.observeAsState()
|
||||||
val user = userState?.user ?: return
|
val user = userState?.user ?: return
|
||||||
|
|
||||||
|
println("AAA Surface recompose")
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
color = MaterialTheme.colors.background
|
color = MaterialTheme.colors.background
|
||||||
@@ -105,14 +108,18 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
|
|||||||
model = banner,
|
model = banner,
|
||||||
contentDescription = "Profile Image",
|
contentDescription = "Profile Image",
|
||||||
contentScale = ContentScale.FillWidth,
|
contentScale = ContentScale.FillWidth,
|
||||||
modifier = Modifier.fillMaxWidth().height(125.dp)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(125.dp)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(R.drawable.profile_banner),
|
painter = painterResource(R.drawable.profile_banner),
|
||||||
contentDescription = "Profile Banner",
|
contentDescription = "Profile Banner",
|
||||||
contentScale = ContentScale.FillWidth,
|
contentScale = ContentScale.FillWidth,
|
||||||
modifier = Modifier.fillMaxWidth().height(125.dp)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(125.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,9 +148,9 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
|
|||||||
NPubCopyButton(user)
|
NPubCopyButton(user)
|
||||||
|
|
||||||
if (accountUser == user) {
|
if (accountUser == user) {
|
||||||
EditButton()
|
EditButton(account)
|
||||||
} else {
|
} else {
|
||||||
if (accountUser.isFollowing(user) == true) {
|
if (accountUser.isFollowing(user)) {
|
||||||
UnfollowButton { account.unfollow(user) }
|
UnfollowButton { account.unfollow(user) }
|
||||||
} else {
|
} else {
|
||||||
FollowButton { account.follow(user) }
|
FollowButton { account.follow(user) }
|
||||||
@@ -300,10 +307,17 @@ private fun MessageButton(user: User, navController: NavController) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun EditButton() {
|
private fun EditButton(account: Account) {
|
||||||
|
var wantsToEdit by remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToEdit)
|
||||||
|
NewUserMetadataView({ wantsToEdit = false }, account)
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.padding(horizontal = 3.dp),
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
onClick = {},
|
onClick = { wantsToEdit = true },
|
||||||
shape = RoundedCornerShape(20.dp),
|
shape = RoundedCornerShape(20.dp),
|
||||||
colors = ButtonDefaults
|
colors = ButtonDefaults
|
||||||
.buttonColors(
|
.buttonColors(
|
||||||
|
Reference in New Issue
Block a user