Add profile detail screen

This commit is contained in:
Kgothatso Ngako
2026-04-01 02:56:58 +02:00
parent b3b93008e5
commit 4d0e67c241
3 changed files with 273 additions and 38 deletions

View File

@@ -3,50 +3,25 @@ package ac.aux.compose.ui.composable
import ac.aux.compose.database.model.NostrEvent
import ac.aux.compose.database.model.Profile
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
import ac.aux.compose.extensions.toFormattedTimeAndDateString
import ac.aux.compose.repository.NostrRepository
import ac.aux.compose.ui.composable.widgets.LoadingDataIndicator
import ac.aux.compose.ui.composable.widgets.detail.TextNoteDetail
import ac.aux.compose.ui.composable.widgets.detail.MetadataEventDetail
import ac.aux.compose.ui.composable.widgets.detail.TextNoteEventDetail
import ac.aux.compose.ui.theme.AuxTheme
import ac.aux.compose.ui.view.model.NostrEventDetailViewModel
import ac.aux.compose.ui.view.state.NostrEventDetailUIState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddReaction
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material.icons.filled.ChatBubble
import androidx.compose.material.icons.filled.CurrencyBitcoin
import androidx.compose.material.icons.filled.Cyclone
import androidx.compose.material.icons.filled.Loop
import androidx.compose.material.icons.filled.MonetizationOn
import androidx.compose.material.icons.filled.Money
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Repeat
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -86,18 +61,13 @@ fun NostrEventDetailScreen(
when(feedListUIState.localNostrEvent.nostrEvent.kind) {
MetadataEvent.KIND -> {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(feedListUIState.localNostrEvent.nostrEvent.content)
Text("profile functionality coming soon.")
}
MetadataEventDetail(
localNostrEvent = feedListUIState.localNostrEvent,
onNavigateBack = onNavigateBack,
)
}
TextNoteEvent.KIND -> {
TextNoteDetail(
TextNoteEventDetail(
feedListUIState.localNostrEvent,
onNavigateBack = onNavigateBack,
onNavigateToWriteAReply = onNavigateToWriteAReply

View File

@@ -0,0 +1,221 @@
package ac.aux.compose.ui.composable.widgets.detail
import ac.aux.compose.database.model.NostrEvent
import ac.aux.compose.database.model.Profile
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
import ac.aux.compose.extensions.toFormattedTimeAndDateString
import ac.aux.compose.ui.theme.AuxTheme
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.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddReaction
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material.icons.filled.ChatBubble
import androidx.compose.material.icons.filled.CurrencyBitcoin
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Repeat
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.ComposeNavigator
import androidx.navigation.compose.DialogNavigator
import androidx.navigation.compose.rememberNavController
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MetadataEventDetail(
localNostrEvent: LocalNostrEvent,
onNavigateBack: () -> Unit,
) {
val profile = localNostrEvent.profile
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
Scaffold(
modifier = Modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
LargeTopAppBar(
navigationIcon = {
IconButton(
onClick = onNavigateBack
) {
Icon(
Icons.Default.ArrowBackIosNew,
contentDescription = "Navigate back"
)
}
},
title = {
Text(
text = profile.displayName ?: profile.userName ?: profile.publicKey,
style = MaterialTheme.typography.titleLarge
)
},
scrollBehavior = scrollBehavior
)
}
) { innerPadding ->
Column(
modifier = Modifier.padding(innerPadding).fillMaxSize()
) {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier.fillMaxWidth().padding(20.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Person,
modifier = Modifier.size(50.dp),
contentDescription = profile.displayName ?: profile.userName ?: "Profile picture"
)
Spacer(
modifier = Modifier.weight(1f)
)
Button(
onClick = {
}
) {
Text(
"Follow"
)
}
}
Text(
modifier = Modifier.fillMaxWidth().padding(
horizontal = 20.dp
),
textAlign = TextAlign.Start,
text = profile.about ?: ""
)
}
}
item {
val destinations = arrayOf("Notes", "Follows", "Following")
val navController = rememberNavController()
val startDestination = 0
val selectedDestination by rememberSaveable {
mutableIntStateOf(startDestination)
}
PrimaryTabRow(
modifier = Modifier.padding(10.dp),
selectedTabIndex = selectedDestination,
) {
destinations.forEachIndexed { index, destination ->
Tab(
selected = selectedDestination == index,
onClick = {
},
text = {
Text(
text = destination,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
)
}
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Spacer(
modifier = Modifier.height(30.dp)
)
Text("Profile Feed Nav Host Goes here...")
}
}
}
}
}
}
@Preview
@Composable
private fun MetadataEventEventDetailPreview() {
AuxTheme {
Surface(
modifier = Modifier.padding(20.dp)
) {
MetadataEventDetail(
localNostrEvent = LocalNostrEvent(
nostrEvent = NostrEvent(
id = "eventId",
pubKey = "publicKey",
content = """Profile""".trimIndent(),
tags = emptyArray(),
sig = "",
kind = MetadataEvent.KIND
),
profile = Profile(
publicKey = "pubKey",
displayName = "John Doe",
nip05 = "john@doe.com",
about = "I am the stone that the builder refused.",
nostrEventId = "nostrEventId"
)
),
onNavigateBack = {},
)
}
}
}

View File

@@ -1,12 +1,17 @@
package ac.aux.compose.ui.composable.widgets.detail
import ac.aux.compose.database.model.NostrEvent
import ac.aux.compose.database.model.Profile
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
import ac.aux.compose.extensions.toFormattedTimeAndDateString
import ac.aux.compose.ui.theme.AuxTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
@@ -26,17 +31,20 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TextNoteDetail(
fun TextNoteEventDetail(
localNostrEvent: LocalNostrEvent,
onNavigateBack: () -> Unit,
onNavigateToWriteAReply: (String) -> Unit
@@ -190,6 +198,7 @@ fun TextNoteDetail(
}
)
},
windowInsets = WindowInsets.navigationBars
)
HorizontalDivider()
@@ -200,4 +209,39 @@ fun TextNoteDetail(
}
}
}
}
@Preview
@Composable
private fun TextNoteEventDetailPreview() {
AuxTheme {
Surface(
modifier = Modifier.padding(20.dp)
) {
TextNoteEventDetail(
localNostrEvent = LocalNostrEvent(
nostrEvent = NostrEvent(
id = "eventId",
pubKey = "publicKey",
content = """Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.""".trimIndent(),
tags = emptyArray(),
sig = "",
kind = TextNoteEvent.KIND
),
profile = Profile(
publicKey = "pubKey",
displayName = "John Doe",
nostrEventId = "nostrEventId"
)
),
onNavigateBack = {},
onNavigateToWriteAReply = {}
)
}
}
}