Add active profile screens
This commit is contained in:
@@ -94,6 +94,7 @@ kotlin {
|
||||
|
||||
implementation(libs.okio)
|
||||
|
||||
implementation(libs.qrose)
|
||||
|
||||
implementation(libs.sqldelight.runtime)
|
||||
implementation(libs.sqldelight.coroutines.extensions)
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
package at.torch.compose.ui.composable
|
||||
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Hub
|
||||
import androidx.compose.material.icons.filled.ImportExport
|
||||
import androidx.compose.material.icons.filled.Key
|
||||
import androidx.compose.material.icons.filled.Logout
|
||||
import androidx.compose.material.icons.filled.QrCode
|
||||
import androidx.compose.material.icons.filled.SwitchVideo
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
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.TextButton
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import at.torch.compose.database.model.NostrEvent
|
||||
import at.torch.compose.database.model.intermdiate.LocalNostrEvent
|
||||
import at.torch.compose.extensions.hexToNpubHrp
|
||||
import at.torch.compose.repository.NostrRepository
|
||||
import at.torch.compose.ui.composable.navigation.routes.ImplementationPendingRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.Route
|
||||
import at.torch.compose.ui.composable.widgets.LoadingDataIndicator
|
||||
import at.torch.compose.ui.composable.widgets.profile.ProfileAvatar
|
||||
import at.torch.compose.ui.view.model.ActiveProfileViewModel
|
||||
import at.torch.compose.ui.view.state.ActiveProfileUIState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ActiveProfileScreen(
|
||||
initialActiveProfileUIState: ActiveProfileUIState = ActiveProfileUIState.Loading,
|
||||
activeUserPublicKey: HexKey,
|
||||
nostrEventId: HexKey,
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToRoute: (Route) -> Unit,
|
||||
nostrRepository: NostrRepository,
|
||||
) {
|
||||
|
||||
val activeProfileViewModel: ActiveProfileViewModel = viewModel(
|
||||
factory = ActiveProfileViewModel.factory(
|
||||
nostrEventId = nostrEventId,
|
||||
initialActiveProfileUIState = initialActiveProfileUIState,
|
||||
nostrRepository = nostrRepository,
|
||||
activeUserPublicKey = activeUserPublicKey
|
||||
),
|
||||
)
|
||||
|
||||
when(val activeProfileUIState = activeProfileViewModel.activeProfileUIState) {
|
||||
ActiveProfileUIState.Error -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text("Something went wrong")
|
||||
}
|
||||
}
|
||||
is ActiveProfileUIState.Loaded -> {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = "Profile"
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onNavigateBack.invoke()
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
contentDescription = "Back"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
ProfileAvatar(
|
||||
profile = activeProfileUIState.localNostrEvent.profile,
|
||||
publicKey = activeUserPublicKey
|
||||
)
|
||||
|
||||
Column {
|
||||
activeProfileUIState.localNostrEvent.profile?.humanReadableNameOrPubkey()?.let { humanReadableNameOrPubkey ->
|
||||
Text(
|
||||
text = humanReadableNameOrPubkey,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
}
|
||||
activeProfileUIState.localNostrEvent.profile?.about?.let {
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.height(10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = activeUserPublicKey.hexToNpubHrp(),
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(10.dp)
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
onNavigateToRoute.invoke(
|
||||
ImplementationPendingRoute("Edit Profile")
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Edit,
|
||||
contentDescription = "Edit profile"
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
"Edit Profile"
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
onNavigateToRoute.invoke(
|
||||
ImplementationPendingRoute("Share and Connect")
|
||||
)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.QrCode,
|
||||
contentDescription = "QR code"
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(10.dp)
|
||||
)
|
||||
Text(
|
||||
"Share Profile"
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
item {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onNavigateToRoute.invoke(
|
||||
ImplementationPendingRoute("Change Account")
|
||||
)
|
||||
}
|
||||
) {
|
||||
|
||||
Icon(
|
||||
Icons.Default.ImportExport,
|
||||
contentDescription = "Change Profile"
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
"Change Account"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
item {
|
||||
TextButton(
|
||||
onClick = {
|
||||
|
||||
onNavigateToRoute.invoke(
|
||||
ImplementationPendingRoute("Profile Keys")
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Key,
|
||||
contentDescription = "Profile Keys"
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(10.dp)
|
||||
)
|
||||
|
||||
Text("Profile Keys")
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onNavigateToRoute.invoke(
|
||||
ImplementationPendingRoute("Network Relays")
|
||||
)
|
||||
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Hub,
|
||||
contentDescription = "Network Relays"
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
"Network Relays"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Button(
|
||||
onClick = {
|
||||
onNavigateToRoute.invoke(
|
||||
ImplementationPendingRoute("Sign Out")
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Logout,
|
||||
contentDescription = "Logout"
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
"Sign out"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ActiveProfileUIState.Loading -> {
|
||||
LoadingDataIndicator()
|
||||
|
||||
LaunchedEffect(true) {
|
||||
activeProfileViewModel.loadNostrFeed()
|
||||
}
|
||||
}
|
||||
ActiveProfileUIState.NotFound -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text("We couldn't find the local profile. Please try again later.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UnannouncedProfileScreenPreview() {
|
||||
_root_ide_package_.at.torch.compose.ui.theme.TorchTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
ActiveProfileScreen(
|
||||
initialActiveProfileUIState = ActiveProfileUIState.Loaded(
|
||||
localNostrEvent = LocalNostrEvent(
|
||||
nostrEvent = NostrEvent(
|
||||
id = "eventId",
|
||||
pubKey = "84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240",
|
||||
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 = MetadataEvent.KIND
|
||||
),
|
||||
profile = _root_ide_package_.at.torch.compose.database.model.Profile(
|
||||
publicKey = "84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240",
|
||||
displayName = "John Doe",
|
||||
nostrEventId = "nostrEventId",
|
||||
about = "I been here, there and everywhere."
|
||||
)
|
||||
|
||||
)
|
||||
),
|
||||
activeUserPublicKey = "84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240",
|
||||
nostrEventId = "",
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
||||
onNavigateToRoute = {},
|
||||
onNavigateBack = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import at.torch.compose.ui.composable.navigation.routes.ActiveProfileRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.NostrEventDetailRoute
|
||||
import at.torch.compose.ui.view.model.HomeScreenType
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
@@ -100,7 +102,7 @@ fun HomeScreen(
|
||||
IconButton(
|
||||
onClick = {
|
||||
onNavigateToEvent.invoke(
|
||||
_root_ide_package_.at.torch.compose.ui.composable.navigation.routes.NostrEventDetailRoute(
|
||||
ActiveProfileRoute(
|
||||
activeUserPublicKey = activeUserPublicKey,
|
||||
nostrEventId = homeScreenUIState.profileWithFollowing.nostrEvent.id
|
||||
)
|
||||
|
||||
@@ -13,12 +13,16 @@ import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.toRoute
|
||||
import at.torch.compose.database.repository.DatabaseMarmotRepository
|
||||
import at.torch.compose.ui.composable.ActiveProfileScreen
|
||||
import at.torch.compose.ui.composable.ChatRoomCreationScreen
|
||||
import at.torch.compose.ui.composable.ChatRoomDetailScreen
|
||||
import at.torch.compose.ui.composable.HomeScreen
|
||||
import at.torch.compose.ui.composable.NostrEventDetailScreen
|
||||
import at.torch.compose.ui.composable.UnsyncedProfileScreen
|
||||
import at.torch.compose.ui.composable.navigation.routes.ActiveProfileRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.ChatRoomCreationRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.LoadingRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.NostrEventDetailRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.SovereignWalletStartupRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.WriteNewNoteRoute
|
||||
import at.torch.compose.ui.view.state.NavigationUIState
|
||||
@@ -443,6 +447,23 @@ fun TorchNavHost(
|
||||
|
||||
}
|
||||
}
|
||||
composable<ActiveProfileRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<NostrEventDetailRoute>()
|
||||
|
||||
ActiveProfileScreen(
|
||||
activeUserPublicKey = route.activeUserPublicKey,
|
||||
nostrEventId = route.nostrEventId,
|
||||
nostrRepository = databaseNostrRepository,
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavigateToRoute = { route ->
|
||||
navController.navigate(
|
||||
route = route
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
composable<at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute>()
|
||||
|
||||
@@ -492,10 +513,10 @@ fun TorchNavHost(
|
||||
}
|
||||
)
|
||||
}
|
||||
composable<at.torch.compose.ui.composable.navigation.routes.NostrEventDetailRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<at.torch.compose.ui.composable.navigation.routes.NostrEventDetailRoute>()
|
||||
composable<NostrEventDetailRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<NostrEventDetailRoute>()
|
||||
|
||||
at.torch.compose.ui.composable.NostrEventDetailScreen(
|
||||
NostrEventDetailScreen(
|
||||
activeUserPublicKey = route.activeUserPublicKey,
|
||||
initialNostrEventDetailUIState = at.torch.compose.ui.view.state.NostrEventDetailUIState.Loading,
|
||||
nostrEventId = route.nostrEventId,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package at.torch.compose.ui.composable.navigation.routes
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ActiveProfileRoute(
|
||||
val activeUserPublicKey: String,
|
||||
val nostrEventId: String,
|
||||
): Route()
|
||||
@@ -0,0 +1,101 @@
|
||||
package at.torch.compose.ui.composable.widgets
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import at.torch.compose.ui.theme.BluePill
|
||||
import io.github.alexzhirkevich.qrose.options.QrBrush
|
||||
import io.github.alexzhirkevich.qrose.options.QrLogoPadding
|
||||
import io.github.alexzhirkevich.qrose.options.QrLogoShape
|
||||
import io.github.alexzhirkevich.qrose.options.solid
|
||||
import io.github.alexzhirkevich.qrose.rememberQrCodePainter
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import torch.composeapp.generated.resources.Res
|
||||
import torch.composeapp.generated.resources.compose_multiplatform
|
||||
|
||||
class QRCodeBackgroundPainter(
|
||||
private val painter: Painter,
|
||||
private val backgroundColor: Color = BluePill,
|
||||
private val padding: Dp = 0.dp,
|
||||
): Painter() {
|
||||
override val intrinsicSize: Size = painter.intrinsicSize
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
val paddingPx = padding.toPx() // Convert Dp to Px
|
||||
|
||||
// Calculate the size excluding the padding
|
||||
val paddedSize = Size(
|
||||
width = size.width - paddingPx * 2,
|
||||
height = size.height - paddingPx * 2
|
||||
)
|
||||
|
||||
// Draw the white background
|
||||
drawRect(
|
||||
color = backgroundColor,
|
||||
size = size
|
||||
)
|
||||
|
||||
// Draw the original painter with the padding
|
||||
with(painter) {
|
||||
// Translate to apply padding
|
||||
translate(left = paddingPx, top = paddingPx) {
|
||||
draw(size = paddedSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun QRCodeView(
|
||||
data: String,
|
||||
) {
|
||||
val qrCodeColor = if (isSystemInDarkTheme()) {
|
||||
Color.White
|
||||
} else {
|
||||
Color.Black
|
||||
}
|
||||
|
||||
val logoPainter = QRCodeBackgroundPainter(
|
||||
painter = painterResource(
|
||||
Res.drawable.compose_multiplatform
|
||||
),
|
||||
)
|
||||
|
||||
val painter = rememberQrCodePainter(
|
||||
data = data,
|
||||
) {
|
||||
logo {
|
||||
painter = logoPainter
|
||||
padding = QrLogoPadding.Natural(.1f)
|
||||
shape = QrLogoShape.Default
|
||||
size = 0.3f
|
||||
}
|
||||
colors {
|
||||
dark = QrBrush.solid(
|
||||
color = qrCodeColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.fillMaxWidth().aspectRatio(1f),
|
||||
painter = painter,
|
||||
contentDescription = data,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package at.torch.compose.ui.view.model
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ActiveProfileViewModel(
|
||||
val activeUserPublicKey: HexKey,
|
||||
val nostrEventId: HexKey,
|
||||
initialActiveProfileUIState: at.torch.compose.ui.view.state.ActiveProfileUIState,
|
||||
val nostrRepository: at.torch.compose.repository.NostrRepository
|
||||
): ViewModel() {
|
||||
var activeProfileUIState: at.torch.compose.ui.view.state.ActiveProfileUIState by mutableStateOf(initialActiveProfileUIState)
|
||||
private set
|
||||
|
||||
fun retryLoad() {
|
||||
activeProfileUIState = at.torch.compose.ui.view.state.ActiveProfileUIState.Loading
|
||||
loadNostrFeed()
|
||||
}
|
||||
|
||||
fun loadNostrFeed() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
||||
// Get contact list and sync feed...
|
||||
nostrRepository.observeLocalNostrEventById(
|
||||
nostrEventId
|
||||
).firstOrNull().let { localNostrEvent ->
|
||||
activeProfileUIState = if (localNostrEvent != null && localNostrEvent.nostrEvent.kind == MetadataEvent.KIND) {
|
||||
at.torch.compose.ui.view.state.ActiveProfileUIState.Loaded(
|
||||
localNostrEvent = localNostrEvent
|
||||
)
|
||||
} else {
|
||||
at.torch.compose.ui.view.state.ActiveProfileUIState.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "ActiveProfileViewModel"
|
||||
|
||||
fun factory(
|
||||
nostrEventId: HexKey,
|
||||
activeUserPublicKey: HexKey,
|
||||
initialActiveProfileUIState: at.torch.compose.ui.view.state.ActiveProfileUIState = at.torch.compose.ui.view.state.ActiveProfileUIState.Loading,
|
||||
nostrRepository: at.torch.compose.repository.NostrRepository,
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
ActiveProfileViewModel(
|
||||
nostrEventId = nostrEventId,
|
||||
initialActiveProfileUIState = initialActiveProfileUIState,
|
||||
nostrRepository = nostrRepository,
|
||||
activeUserPublicKey = activeUserPublicKey
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package at.torch.compose.ui.view.state
|
||||
|
||||
sealed interface ActiveProfileUIState {
|
||||
data class Loaded(
|
||||
val localNostrEvent: at.torch.compose.database.model.intermdiate.LocalNostrEvent
|
||||
): ActiveProfileUIState
|
||||
|
||||
data object Error: ActiveProfileUIState
|
||||
data object NotFound: ActiveProfileUIState
|
||||
data object Loading: ActiveProfileUIState
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ navigationCompose = "2.9.2"
|
||||
okhttp = "5.3.2"
|
||||
okio = "3.17.0"
|
||||
pagingCommon = "3.5.0"
|
||||
qrose = "1.1.2"
|
||||
quartz = "1.10.0"
|
||||
room3 = "3.0.0-alpha06"
|
||||
sqldelight = "2.3.2"
|
||||
@@ -79,6 +80,7 @@ lightning-kmp-core = { module = "fr.acinq.lightning:lightning-kmp-core", version
|
||||
navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||
okhttp-coroutines = { group = "com.squareup.okhttp3", name = "okhttp-coroutines", version.ref = "okhttp" }
|
||||
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
|
||||
qrose = { module = "io.github.alexzhirkevich:qrose", version.ref = "qrose" }
|
||||
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||
sqldelight-coroutines-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
|
||||
sqldelight-native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqldelight" }
|
||||
|
||||
Reference in New Issue
Block a user