Migrates DVM route from pubkey to AppDefinition eventId and makes sure NFC reading will load the AppDefinition itself before rendering the rest of the screen.
This commit is contained in:
@@ -229,7 +229,7 @@ fun AppNavigation(
|
|||||||
content = {
|
content = {
|
||||||
it.arguments?.getString("id")?.let { id ->
|
it.arguments?.getString("id")?.let { id ->
|
||||||
NIP90ContentDiscoveryScreen(
|
NIP90ContentDiscoveryScreen(
|
||||||
dvmPublicKey = id,
|
appDefinitionEventId = id,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ExpandMore
|
import androidx.compose.material.icons.filled.ExpandMore
|
||||||
@@ -60,11 +62,13 @@ 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
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
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.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@@ -73,6 +77,7 @@ import androidx.lifecycle.map
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import coil.Coil
|
import coil.Coil
|
||||||
|
import coil.compose.AsyncImage
|
||||||
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.AddressableNote
|
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||||
@@ -97,6 +102,7 @@ import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
|||||||
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
||||||
import com.vitorpamplona.amethyst.service.relays.Client
|
import com.vitorpamplona.amethyst.service.relays.Client
|
||||||
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||||
import com.vitorpamplona.amethyst.ui.note.AmethystIcon
|
import com.vitorpamplona.amethyst.ui.note.AmethystIcon
|
||||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||||
@@ -106,6 +112,7 @@ import com.vitorpamplona.amethyst.ui.note.LoadChannel
|
|||||||
import com.vitorpamplona.amethyst.ui.note.LoadCityName
|
import com.vitorpamplona.amethyst.ui.note.LoadCityName
|
||||||
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
||||||
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
|
||||||
import com.vitorpamplona.amethyst.ui.note.SearchIcon
|
import com.vitorpamplona.amethyst.ui.note.SearchIcon
|
||||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||||
@@ -122,6 +129,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.LongRoomHeader
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.RoomNameOnlyDisplay
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.RoomNameOnlyDisplay
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShortChannelHeader
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShortChannelHeader
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.observeAppDefinition
|
||||||
import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight
|
import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||||
@@ -297,26 +305,34 @@ private fun RoomTopBar(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DvmTopBar(
|
private fun DvmTopBar(
|
||||||
id: String,
|
appDefinitionId: String,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
navPopBack: () -> Unit,
|
navPopBack: () -> Unit,
|
||||||
) {
|
) {
|
||||||
FlexibleTopBarWithBackButton(
|
FlexibleTopBarWithBackButton(
|
||||||
title = {
|
title = {
|
||||||
LoadUser(baseUserHex = id, accountViewModel) { baseUser ->
|
LoadNote(baseNoteHex = appDefinitionId, accountViewModel = accountViewModel) { appDefinitionNote ->
|
||||||
if (baseUser != null) {
|
if (appDefinitionNote != null) {
|
||||||
ClickableUserPicture(
|
val card = observeAppDefinition(appDefinitionNote)
|
||||||
baseUser = baseUser,
|
|
||||||
accountViewModel = accountViewModel,
|
card.cover?.let {
|
||||||
size = Size34dp,
|
AsyncImage(
|
||||||
|
model = it,
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.size(Size34dp).clip(shape = CircleShape),
|
||||||
)
|
)
|
||||||
|
} ?: run { NoteAuthorPicture(baseNote = appDefinitionNote, size = Size34dp, accountViewModel = accountViewModel) }
|
||||||
|
|
||||||
Spacer(modifier = DoubleHorzSpacer)
|
Spacer(modifier = DoubleHorzSpacer)
|
||||||
|
|
||||||
UsernameDisplay(baseUser, Modifier.weight(1f), fontWeight = FontWeight.Normal)
|
Text(
|
||||||
} else {
|
text = card.name,
|
||||||
Spacer(BottomTopHeight)
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.EndedFlag
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LiveFlag
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LiveFlag
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.OfflineFlag
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.OfflineFlag
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ScheduledFlag
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ScheduledFlag
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.observeAppDefinition
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.showAmountAxis
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.showAmountAxis
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.HalfPadding
|
import com.vitorpamplona.amethyst.ui.theme.HalfPadding
|
||||||
@@ -342,7 +343,9 @@ fun InnerRenderClassifiedsThumb(
|
|||||||
note: Note,
|
note: Note,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
Modifier.fillMaxWidth().aspectRatio(1f),
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(1f),
|
||||||
contentAlignment = BottomStart,
|
contentAlignment = BottomStart,
|
||||||
) {
|
) {
|
||||||
card.image?.let {
|
card.image?.let {
|
||||||
@@ -355,7 +358,10 @@ fun InnerRenderClassifiedsThumb(
|
|||||||
} ?: run { DisplayAuthorBanner(note) }
|
} ?: run { DisplayAuthorBanner(note) }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().background(Color.Black.copy(0.6f)).padding(Size5dp),
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(Color.Black.copy(0.6f))
|
||||||
|
.padding(Size5dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
card.title?.let {
|
card.title?.let {
|
||||||
@@ -451,14 +457,20 @@ fun RenderLiveActivityThumb(
|
|||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
contentAlignment = TopEnd,
|
contentAlignment = TopEnd,
|
||||||
modifier = Modifier.aspectRatio(ratio = 16f / 9f).fillMaxWidth(),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.aspectRatio(ratio = 16f / 9f)
|
||||||
|
.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
card.cover?.let {
|
card.cover?.let {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = it,
|
model = it,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier.fillMaxSize().clip(QuoteBorder),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(QuoteBorder),
|
||||||
)
|
)
|
||||||
} ?: run { DisplayAuthorBanner(baseNote) }
|
} ?: run { DisplayAuthorBanner(baseNote) }
|
||||||
|
|
||||||
@@ -494,7 +506,9 @@ fun RenderLiveActivityThumb(
|
|||||||
|
|
||||||
LoadParticipants(card.participants, baseNote, accountViewModel) { participantUsers ->
|
LoadParticipants(card.participants, baseNote, accountViewModel) { participantUsers ->
|
||||||
Box(
|
Box(
|
||||||
Modifier.padding(10.dp).align(BottomStart),
|
Modifier
|
||||||
|
.padding(10.dp)
|
||||||
|
.align(BottomStart),
|
||||||
) {
|
) {
|
||||||
if (participantUsers.isNotEmpty()) {
|
if (participantUsers.isNotEmpty()) {
|
||||||
Gallery(participantUsers, accountViewModel)
|
Gallery(participantUsers, accountViewModel)
|
||||||
@@ -572,7 +586,10 @@ fun RenderCommunitiesThumb(
|
|||||||
model = it,
|
model = it,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier.fillMaxSize().clip(QuoteBorder),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(QuoteBorder),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: run { DisplayAuthorBanner(baseNote) }
|
} ?: run { DisplayAuthorBanner(baseNote) }
|
||||||
@@ -742,29 +759,7 @@ fun RenderContentDVMThumb(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val noteEvent = baseNote.event as? AppDefinitionEvent ?: return
|
val card = observeAppDefinition(appDefinitionNote = baseNote)
|
||||||
|
|
||||||
val card by
|
|
||||||
baseNote
|
|
||||||
.live()
|
|
||||||
.metadata
|
|
||||||
.map {
|
|
||||||
val noteEvent = it.note.event as? AppDefinitionEvent
|
|
||||||
|
|
||||||
DVMCard(
|
|
||||||
name = noteEvent?.appMetaData()?.name ?: "",
|
|
||||||
description = noteEvent?.appMetaData()?.about ?: "",
|
|
||||||
cover = noteEvent?.appMetaData()?.image?.ifBlank { null },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.distinctUntilChanged()
|
|
||||||
.observeAsState(
|
|
||||||
DVMCard(
|
|
||||||
name = noteEvent.appMetaData()?.name ?: "",
|
|
||||||
description = noteEvent.appMetaData()?.about ?: "",
|
|
||||||
cover = noteEvent.appMetaData()?.image?.ifBlank { null },
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
LeftPictureLayout(
|
LeftPictureLayout(
|
||||||
onImage = {
|
onImage = {
|
||||||
@@ -774,7 +769,10 @@ fun RenderContentDVMThumb(
|
|||||||
model = it,
|
model = it,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier.fillMaxSize().clip(QuoteBorder),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(QuoteBorder),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: run { DisplayAuthorBanner(baseNote) }
|
} ?: run { DisplayAuthorBanner(baseNote) }
|
||||||
@@ -788,7 +786,7 @@ fun RenderContentDVMThumb(
|
|||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = StdHorzSpacer)
|
Spacer(modifier = StdVertSpacer)
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = RowColSpacing,
|
horizontalArrangement = RowColSpacing,
|
||||||
@@ -895,7 +893,10 @@ fun RenderChannelThumb(
|
|||||||
model = it,
|
model = it,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier.fillMaxSize().clip(QuoteBorder),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(QuoteBorder),
|
||||||
)
|
)
|
||||||
} ?: run { DisplayAuthorBanner(baseNote) }
|
} ?: run { DisplayAuthorBanner(baseNote) }
|
||||||
},
|
},
|
||||||
@@ -970,6 +971,11 @@ fun Gallery(
|
|||||||
@Composable
|
@Composable
|
||||||
fun DisplayAuthorBanner(note: Note) {
|
fun DisplayAuthorBanner(note: Note) {
|
||||||
WatchAuthor(note) {
|
WatchAuthor(note) {
|
||||||
BannerImage(it, Modifier.fillMaxSize().clip(QuoteBorder))
|
BannerImage(
|
||||||
|
it,
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(QuoteBorder),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,16 +39,10 @@ fun WatchNoteEvent(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onNoteEventFound: @Composable () -> Unit,
|
onNoteEventFound: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
if (baseNote.event != null) {
|
WatchNoteEvent(
|
||||||
onNoteEventFound()
|
baseNote,
|
||||||
} else {
|
onNoteEventFound,
|
||||||
// avoid observing costs if already has an event.
|
onBlank = {
|
||||||
|
|
||||||
val hasEvent by baseNote.live().hasEvent.observeAsState(baseNote.event != null)
|
|
||||||
Crossfade(targetState = hasEvent, label = "Event presence") {
|
|
||||||
if (it) {
|
|
||||||
onNoteEventFound()
|
|
||||||
} else {
|
|
||||||
LongPressToQuickAction(
|
LongPressToQuickAction(
|
||||||
baseNote = baseNote,
|
baseNote = baseNote,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
@@ -62,6 +56,27 @@ fun WatchNoteEvent(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WatchNoteEvent(
|
||||||
|
baseNote: Note,
|
||||||
|
onNoteEventFound: @Composable () -> Unit,
|
||||||
|
onBlank: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
if (baseNote.event != null) {
|
||||||
|
onNoteEventFound()
|
||||||
|
} else {
|
||||||
|
// avoid observing costs if already has an event.
|
||||||
|
|
||||||
|
val hasEvent by baseNote.live().hasEvent.observeAsState(baseNote.event != null)
|
||||||
|
Crossfade(targetState = hasEvent, label = "Event presence") {
|
||||||
|
if (it) {
|
||||||
|
onNoteEventFound()
|
||||||
|
} else {
|
||||||
|
onBlank()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+106
-32
@@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -36,45 +37,77 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
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.layout.ContentScale
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.lifecycle.distinctUntilChanged
|
||||||
|
import androidx.lifecycle.map
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import coil.compose.AsyncImage
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
import com.vitorpamplona.amethyst.ui.note.DVMCard
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
|
||||||
import com.vitorpamplona.amethyst.ui.screen.FeedEmpty
|
import com.vitorpamplona.amethyst.ui.screen.FeedEmpty
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrNIP90ContentDiscoveryFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrNIP90ContentDiscoveryFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.RefresheableBox
|
import com.vitorpamplona.amethyst.ui.screen.RefresheableBox
|
||||||
import com.vitorpamplona.amethyst.ui.screen.RenderFeedState
|
import com.vitorpamplona.amethyst.ui.screen.RenderFeedState
|
||||||
import com.vitorpamplona.amethyst.ui.screen.SaveableFeedState
|
import com.vitorpamplona.amethyst.ui.screen.SaveableFeedState
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.events.AppDefinitionEvent
|
||||||
import com.vitorpamplona.quartz.events.NIP90ContentDiscoveryResponseEvent
|
import com.vitorpamplona.quartz.events.NIP90ContentDiscoveryResponseEvent
|
||||||
import com.vitorpamplona.quartz.events.NIP90StatusEvent
|
import com.vitorpamplona.quartz.events.NIP90StatusEvent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NIP90ContentDiscoveryScreen(
|
fun NIP90ContentDiscoveryScreen(
|
||||||
dvmPublicKey: String,
|
appDefinitionEventId: String,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
LoadNote(baseNoteHex = appDefinitionEventId, accountViewModel = accountViewModel) {
|
||||||
|
it?.let { baseNote ->
|
||||||
|
WatchNoteEvent(
|
||||||
|
baseNote,
|
||||||
|
onNoteEventFound = {
|
||||||
|
NIP90ContentDiscoveryScreen(baseNote, accountViewModel, nav)
|
||||||
|
},
|
||||||
|
onBlank = {
|
||||||
|
FeedEmptywithStatus(baseNote, stringResource(R.string.dvm_looking_for_app), accountViewModel, nav)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NIP90ContentDiscoveryScreen(
|
||||||
|
appDefinition: Note,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val noteAuthor = appDefinition.author ?: return
|
||||||
|
|
||||||
var requestEventID by
|
var requestEventID by
|
||||||
remember(dvmPublicKey) {
|
remember(appDefinition) {
|
||||||
mutableStateOf<Note?>(null)
|
mutableStateOf<Note?>(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val onRefresh = {
|
val onRefresh = {
|
||||||
accountViewModel.requestDVMContentDiscovery(dvmPublicKey) {
|
accountViewModel.requestDVMContentDiscovery(noteAuthor.pubkeyHex) {
|
||||||
requestEventID = it
|
requestEventID = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(key1 = dvmPublicKey) {
|
LaunchedEffect(key1 = appDefinition) {
|
||||||
onRefresh()
|
onRefresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +117,7 @@ fun NIP90ContentDiscoveryScreen(
|
|||||||
val myRequestEventID = requestEventID
|
val myRequestEventID = requestEventID
|
||||||
if (myRequestEventID != null) {
|
if (myRequestEventID != null) {
|
||||||
ObserverContentDiscoveryResponse(
|
ObserverContentDiscoveryResponse(
|
||||||
dvmPublicKey,
|
appDefinition,
|
||||||
myRequestEventID,
|
myRequestEventID,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
@@ -92,19 +125,20 @@ fun NIP90ContentDiscoveryScreen(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// TODO: Make a good splash screen with loading animation for this DVM.
|
// TODO: Make a good splash screen with loading animation for this DVM.
|
||||||
FeedEmptywithStatus(dvmPublicKey, stringResource(R.string.dvm_requesting_job), accountViewModel, nav)
|
FeedEmptywithStatus(appDefinition, stringResource(R.string.dvm_requesting_job), accountViewModel, nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ObserverContentDiscoveryResponse(
|
fun ObserverContentDiscoveryResponse(
|
||||||
dvmPublicKey: String,
|
appDefinition: Note,
|
||||||
dvmRequestId: Note,
|
dvmRequestId: Note,
|
||||||
onRefresh: () -> Unit,
|
onRefresh: () -> Unit,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
val noteAuthor = appDefinition.author ?: return
|
||||||
val updateFiltersFromRelays = dvmRequestId.live().metadata.observeAsState()
|
val updateFiltersFromRelays = dvmRequestId.live().metadata.observeAsState()
|
||||||
|
|
||||||
val resultFlow =
|
val resultFlow =
|
||||||
@@ -116,7 +150,7 @@ fun ObserverContentDiscoveryResponse(
|
|||||||
|
|
||||||
if (latestResponse != null) {
|
if (latestResponse != null) {
|
||||||
PrepareViewContentDiscoveryModels(
|
PrepareViewContentDiscoveryModels(
|
||||||
dvmPublicKey,
|
noteAuthor,
|
||||||
dvmRequestId.idHex,
|
dvmRequestId.idHex,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
@@ -124,7 +158,7 @@ fun ObserverContentDiscoveryResponse(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ObserverDvmStatusResponse(
|
ObserverDvmStatusResponse(
|
||||||
dvmPublicKey,
|
appDefinition,
|
||||||
dvmRequestId.idHex,
|
dvmRequestId.idHex,
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
nav,
|
nav,
|
||||||
@@ -134,7 +168,7 @@ fun ObserverContentDiscoveryResponse(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ObserverDvmStatusResponse(
|
fun ObserverDvmStatusResponse(
|
||||||
dvmPublicKey: String,
|
appDefinition: Note,
|
||||||
dvmRequestId: String,
|
dvmRequestId: String,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
@@ -149,17 +183,17 @@ fun ObserverDvmStatusResponse(
|
|||||||
if (latestStatus != null) {
|
if (latestStatus != null) {
|
||||||
// TODO: Make a good splash screen with loading animation for this DVM.
|
// TODO: Make a good splash screen with loading animation for this DVM.
|
||||||
latestStatus?.let {
|
latestStatus?.let {
|
||||||
FeedEmptywithStatus(dvmPublicKey, it.content(), accountViewModel, nav)
|
FeedEmptywithStatus(appDefinition, it.content(), accountViewModel, nav)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: Make a good splash screen with loading animation for this DVM.
|
// TODO: Make a good splash screen with loading animation for this DVM.
|
||||||
FeedEmptywithStatus(dvmPublicKey, stringResource(R.string.dvm_waiting_status), accountViewModel, nav)
|
FeedEmptywithStatus(appDefinition, stringResource(R.string.dvm_waiting_status), accountViewModel, nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PrepareViewContentDiscoveryModels(
|
fun PrepareViewContentDiscoveryModels(
|
||||||
dvmPublicKey: String,
|
dvm: User,
|
||||||
dvmRequestId: String,
|
dvmRequestId: String,
|
||||||
onRefresh: () -> Unit,
|
onRefresh: () -> Unit,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
@@ -167,8 +201,8 @@ fun PrepareViewContentDiscoveryModels(
|
|||||||
) {
|
) {
|
||||||
val resultFeedViewModel: NostrNIP90ContentDiscoveryFeedViewModel =
|
val resultFeedViewModel: NostrNIP90ContentDiscoveryFeedViewModel =
|
||||||
viewModel(
|
viewModel(
|
||||||
key = "NostrNIP90ContentDiscoveryFeedViewModel$dvmPublicKey$dvmRequestId",
|
key = "NostrNIP90ContentDiscoveryFeedViewModel${dvm.pubkeyHex}$dvmRequestId",
|
||||||
factory = NostrNIP90ContentDiscoveryFeedViewModel.Factory(accountViewModel.account, dvmkey = dvmPublicKey, requestid = dvmRequestId),
|
factory = NostrNIP90ContentDiscoveryFeedViewModel.Factory(accountViewModel.account, dvmkey = dvm.pubkeyHex, requestid = dvmRequestId),
|
||||||
)
|
)
|
||||||
|
|
||||||
LaunchedEffect(key1 = dvmRequestId) {
|
LaunchedEffect(key1 = dvmRequestId) {
|
||||||
@@ -210,7 +244,7 @@ fun RenderNostrNIP90ContentDiscoveryScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FeedEmptywithStatus(
|
fun FeedEmptywithStatus(
|
||||||
pubkey: HexKey,
|
appDefinitionNote: Note,
|
||||||
status: String,
|
status: String,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
@@ -222,22 +256,62 @@ fun FeedEmptywithStatus(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
LoadUser(baseUserHex = pubkey, accountViewModel = accountViewModel) { baseUser ->
|
val card = observeAppDefinition(appDefinitionNote)
|
||||||
if (baseUser != null) {
|
|
||||||
ClickableUserPicture(
|
card.cover?.let {
|
||||||
baseUser = baseUser,
|
AsyncImage(
|
||||||
accountViewModel = accountViewModel,
|
model = it,
|
||||||
size = Size75dp,
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.size(Size75dp).clip(QuoteBorder),
|
||||||
|
)
|
||||||
|
} ?: run { NoteAuthorPicture(appDefinitionNote, nav, accountViewModel, Size75dp) }
|
||||||
|
|
||||||
|
Spacer(modifier = DoubleVertSpacer)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = card.name,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
Spacer(modifier = DoubleVertSpacer)
|
||||||
|
|
||||||
UsernameDisplay(baseUser, Modifier, fontWeight = FontWeight.Normal)
|
|
||||||
|
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(status)
|
Text(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun observeAppDefinition(appDefinitionNote: Note): DVMCard {
|
||||||
|
val noteEvent =
|
||||||
|
appDefinitionNote.event as? AppDefinitionEvent ?: return DVMCard(
|
||||||
|
name = "",
|
||||||
|
description = "",
|
||||||
|
cover = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val card by
|
||||||
|
appDefinitionNote
|
||||||
|
.live()
|
||||||
|
.metadata
|
||||||
|
.map {
|
||||||
|
val noteEvent = it.note.event as? AppDefinitionEvent
|
||||||
|
|
||||||
|
DVMCard(
|
||||||
|
name = noteEvent?.appMetaData()?.name ?: "",
|
||||||
|
description = noteEvent?.appMetaData()?.about ?: "",
|
||||||
|
cover = noteEvent?.appMetaData()?.image?.ifBlank { null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.observeAsState(
|
||||||
|
DVMCard(
|
||||||
|
name = noteEvent.appMetaData()?.name ?: "",
|
||||||
|
description = noteEvent.appMetaData()?.about ?: "",
|
||||||
|
cover = noteEvent.appMetaData()?.image?.ifBlank { null },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return card
|
||||||
|
}
|
||||||
|
|||||||
@@ -847,6 +847,7 @@
|
|||||||
|
|
||||||
<string name="load_from_text">From Msg</string>
|
<string name="load_from_text">From Msg</string>
|
||||||
|
|
||||||
|
<string name="dvm_looking_for_app">Looking for Application</string>
|
||||||
<string name="dvm_waiting_status">Job Requested, waiting for a reply</string>
|
<string name="dvm_waiting_status">Job Requested, waiting for a reply</string>
|
||||||
<string name="dvm_requesting_job">Requesting Job from DVM</string>
|
<string name="dvm_requesting_job">Requesting Job from DVM</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user