mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-28 06:17:07 +02:00
Simplifying badge composables
This commit is contained in:
@@ -27,6 +27,7 @@ import androidx.compose.runtime.ProduceStateScope
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -43,9 +44,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@Composable
|
||||
@@ -94,20 +93,13 @@ fun LoadAddressableNote(
|
||||
accountViewModel: AccountViewModel,
|
||||
content: @Composable (AddressableNote?) -> Unit,
|
||||
) {
|
||||
var note by
|
||||
remember(address) {
|
||||
mutableStateOf(accountViewModel.getAddressableNoteIfExists(address))
|
||||
}
|
||||
|
||||
if (note == null) {
|
||||
LaunchedEffect(key1 = address) {
|
||||
val newNote =
|
||||
withContext(Dispatchers.IO) {
|
||||
accountViewModel.getOrCreateAddressableNote(address)
|
||||
}
|
||||
if (note != newNote) {
|
||||
note = newNote
|
||||
}
|
||||
val note by produceState(
|
||||
accountViewModel.getAddressableNoteIfExists(address),
|
||||
address,
|
||||
) {
|
||||
val newNote = accountViewModel.getOrCreateAddressableNote(address)
|
||||
if (newNote != value) {
|
||||
value = newNote
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,25 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.badges
|
||||
|
||||
import android.R.attr.onClick
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
@@ -46,23 +39,24 @@ import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEventAndMap
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav.nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.BadgePictureModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size35Modifier
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||
import com.vitorpamplona.quartz.nip58Badges.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.BadgeDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.BadgeProfilesEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
fun DisplayBadges(
|
||||
@@ -114,17 +108,20 @@ private fun LoadAndRenderBadge(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
var baseNote by remember(badgeAwardEvent) { mutableStateOf(LocalCache.getNoteIfExists(badgeAwardEvent)) }
|
||||
|
||||
LaunchedEffect(key1 = badgeAwardEvent) {
|
||||
if (baseNote == null) {
|
||||
withContext(Dispatchers.IO) {
|
||||
baseNote = LocalCache.checkGetOrCreateNote(badgeAwardEvent)
|
||||
val baseNote =
|
||||
produceState(
|
||||
LocalCache.getNoteIfExists(badgeAwardEvent),
|
||||
badgeAwardEvent,
|
||||
) {
|
||||
val newValue = LocalCache.checkGetOrCreateNote(badgeAwardEvent)
|
||||
if (newValue != value) {
|
||||
value = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baseNote?.let { ObserveAndRenderBadge(it, accountViewModel, nav) }
|
||||
baseNote.value?.let {
|
||||
ObserveAndRenderBadge(it, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -133,90 +130,80 @@ private fun ObserveAndRenderBadge(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val badgeAwardState by observeNote(it, accountViewModel)
|
||||
val baseBadgeDefinition = badgeAwardState.note.replyTo?.firstOrNull()
|
||||
baseBadgeDefinition?.let { BadgeThumb(it, accountViewModel, nav, Size35dp) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BadgeThumb(
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
size: Dp,
|
||||
pictureModifier: Modifier = Modifier,
|
||||
) {
|
||||
BadgeThumb(note, accountViewModel, size, pictureModifier) { nav.nav(Route.Note(note.idHex)) }
|
||||
val badgeAwardState by observeNoteEvent<BadgeAwardEvent>(it, accountViewModel)
|
||||
val badgeDefinitionId = badgeAwardState?.awardDefinition()?.firstOrNull()
|
||||
if (badgeDefinitionId != null) {
|
||||
LoadAddressableNote(badgeDefinitionId, accountViewModel) { badgeDefNote ->
|
||||
badgeDefNote?.let {
|
||||
BadgeThumb(it, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BadgeThumb(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
size: Dp,
|
||||
pictureModifier: Modifier = Modifier,
|
||||
onClick: ((String) -> Unit)? = null,
|
||||
nav: INav,
|
||||
) {
|
||||
Box(
|
||||
remember {
|
||||
Modifier
|
||||
.width(size)
|
||||
.height(size)
|
||||
},
|
||||
modifier =
|
||||
Size35Modifier.clickable(
|
||||
onClick = {
|
||||
nav.nav {
|
||||
routeFor(baseNote, accountViewModel.account)
|
||||
}
|
||||
},
|
||||
),
|
||||
) {
|
||||
WatchAndRenderBadgeImage(baseNote, size, pictureModifier, accountViewModel, onClick)
|
||||
WatchAndRenderBadgeImage(baseNote, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WatchAndRenderBadgeImage(
|
||||
baseNote: Note,
|
||||
size: Dp,
|
||||
pictureModifier: Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClick: ((String) -> Unit)?,
|
||||
) {
|
||||
val noteState by observeNote(baseNote, accountViewModel)
|
||||
val eventId = remember(noteState) { noteState?.note?.idHex } ?: return
|
||||
val image by
|
||||
remember(noteState) {
|
||||
derivedStateOf {
|
||||
val event = noteState.note.event as? BadgeDefinitionEvent
|
||||
val event by observeNoteEvent<BadgeDefinitionEvent>(baseNote, accountViewModel)
|
||||
|
||||
event?.let {
|
||||
val image =
|
||||
remember(event) {
|
||||
event?.thumb()?.ifBlank { null } ?: event?.image()?.ifBlank { null }
|
||||
}
|
||||
RenderBadgeImage(it.id, it.name(), image, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderBadgeImage(
|
||||
id: String,
|
||||
name: String?,
|
||||
image: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val description =
|
||||
if (name != null) {
|
||||
stringRes(id = R.string.badge_award_image_for, name)
|
||||
} else {
|
||||
stringRes(id = R.string.badge_award_image)
|
||||
}
|
||||
|
||||
if (image == null) {
|
||||
RobohashAsyncImage(
|
||||
robot = "authornotfound",
|
||||
contentDescription = stringRes(R.string.unknown_author),
|
||||
modifier =
|
||||
remember {
|
||||
pictureModifier
|
||||
.width(size)
|
||||
.height(size)
|
||||
},
|
||||
robot = "badgenotfound",
|
||||
contentDescription = description,
|
||||
modifier = BadgePictureModifier,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
} else {
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = eventId,
|
||||
model = image!!,
|
||||
contentDescription = stringRes(id = R.string.profile_image),
|
||||
modifier =
|
||||
remember {
|
||||
pictureModifier
|
||||
.width(size)
|
||||
.height(size)
|
||||
.clip(shape = CutCornerShape(20))
|
||||
.run {
|
||||
if (onClick != null) {
|
||||
this.clickable(onClick = { onClick(eventId) })
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
},
|
||||
robot = id,
|
||||
model = image,
|
||||
contentDescription = description,
|
||||
modifier = BadgePictureModifier,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
|
@@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Shapes
|
||||
@@ -344,3 +345,5 @@ val SimpleImage35Modifier = Modifier.size(Size34dp).clip(shape = CircleShape)
|
||||
val SimpleImageBorder = Modifier.fillMaxSize().clip(QuoteBorder)
|
||||
|
||||
val SimpleHeaderImage = Modifier.fillMaxWidth().heightIn(max = 200.dp)
|
||||
|
||||
val BadgePictureModifier = Modifier.size(35.dp).clip(shape = CutCornerShape(20))
|
||||
|
@@ -311,6 +311,7 @@
|
||||
<string name="biometric_error">Error</string>
|
||||
<string name="badge_created_by">"Created by %1$s"</string>
|
||||
<string name="badge_award_image_for">"Badge award image for %1$s"</string>
|
||||
<string name="badge_award_image">"Badge award image</string>
|
||||
<string name="new_badge_award_notif">You Received a new Badge Award</string>
|
||||
<string name="award_granted_to">Badge award granted to</string>
|
||||
<string name="copied_note_text_to_clipboard">Copied note text to clipboard</string>
|
||||
|
Reference in New Issue
Block a user