diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapUserSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapUserSetCompose.kt new file mode 100644 index 000000000..f9512cde8 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapUserSetCompose.kt @@ -0,0 +1,109 @@ +package com.vitorpamplona.amethyst.ui.note + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bolt +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.compositeOver +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.google.accompanist.flowlayout.FlowRow +import com.vitorpamplona.amethyst.NotificationCache +import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.screen.ZapUserSetCard +import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun ZapUserSetCompose(zapSetCard: ZapUserSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) { + val accountState by accountViewModel.accountLiveData.observeAsState() + val account = accountState?.account ?: return + + var isNew by remember { mutableStateOf(false) } + + LaunchedEffect(key1 = zapSetCard) { + withContext(Dispatchers.IO) { + isNew = zapSetCard.createdAt > NotificationCache.load(routeForLastRead) + + NotificationCache.markAsRead(routeForLastRead, zapSetCard.createdAt) + } + } + + var backgroundColor = if (isNew) { + MaterialTheme.colors.primary.copy(0.12f).compositeOver(MaterialTheme.colors.background) + } else { + MaterialTheme.colors.background + } + + Column( + modifier = Modifier + .background(backgroundColor) + .clickable { + navController.navigate("User/${zapSetCard.user.pubkeyHex}") + } + ) { + Row( + modifier = Modifier + .padding( + start = if (!isInnerNote) 12.dp else 0.dp, + end = if (!isInnerNote) 12.dp else 0.dp, + top = 10.dp + ) + ) { + // Draws the like picture outside the boosted card. + if (!isInnerNote) { + Box( + modifier = Modifier + .width(55.dp) + .padding(0.dp) + ) { + Icon( + imageVector = Icons.Default.Bolt, + contentDescription = stringResource(id = R.string.zaps), + tint = BitcoinOrange, + modifier = Modifier + .size(25.dp) + .align(Alignment.TopEnd) + ) + } + } + + Column(modifier = Modifier.padding(start = if (!isInnerNote) 10.dp else 0.dp)) { + FlowRow() { + zapSetCard.zapEvents.forEach { + NoteAuthorPicture( + note = it.key, + navController = navController, + userAccount = account.userProfile(), + size = 35.dp + ) + } + } + + UserCompose(baseUser = zapSetCard.user, accountViewModel = accountViewModel, navController = navController) + } + } + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt index bdacb2639..3f374730c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt @@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.screen import androidx.compose.runtime.MutableState import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.model.User abstract class Card() { abstract fun createdAt(): Long @@ -40,6 +41,14 @@ class ZapSetCard(val note: Note, val zapEvents: Map) : Card() { override fun id() = note.idHex + "Z" + createdAt } +class ZapUserSetCard(val user: User, val zapEvents: Map) : Card() { + val createdAt = zapEvents.maxOf { it.value.createdAt() ?: 0 } + override fun createdAt(): Long { + return createdAt + } + override fun id() = user.pubkeyHex + "U" + createdAt +} + class MultiSetCard(val note: Note, val boostEvents: List, val likeEvents: List, val zapEvents: Map) : Card() { val createdAt = maxOf( zapEvents.maxOfOrNull { it.value.createdAt() ?: 0 } ?: 0, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt index ed5e46c62..fb3797759 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt @@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.ui.note.MessageSetCompose import com.vitorpamplona.amethyst.ui.note.MultiSetCompose import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.note.ZapSetCompose +import com.vitorpamplona.amethyst.ui.note.ZapUserSetCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @OptIn(ExperimentalMaterialApi::class) @@ -128,6 +129,13 @@ private fun FeedLoaded( navController = navController, routeForLastRead = routeForLastRead ) + is ZapUserSetCard -> ZapUserSetCompose( + item, + isInnerNote = false, + accountViewModel = accountViewModel, + navController = navController, + routeForLastRead = routeForLastRead + ) is LikeSetCard -> LikeSetCompose( item, isInnerNote = false, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index 4e547522d..3c83d0945 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -7,6 +7,7 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCacheState import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent @@ -78,7 +79,7 @@ open class CardFeedViewModel(val dataSource: FeedFilter) : ViewModel() { } // val reactionCards = reactionsPerEvent.map { LikeSetCard(it.key, it.value) } - + val zapsPerUser = mutableMapOf>() val zapsPerEvent = mutableMapOf>() notes .filter { it.event is LnZapEvent } @@ -89,6 +90,20 @@ open class CardFeedViewModel(val dataSource: FeedFilter) : ViewModel() { if (zapRequest != null) { zapsPerEvent.getOrPut(zappedPost, { mutableMapOf() }).put(zapRequest, zapEvent) } + } else { + val event = (zapEvent.event as LnZapEvent) + val author = event.zappedAuthor().mapNotNull { + LocalCache.checkGetOrCreateUser( + it + ) + }.firstOrNull() + if (author != null) { + val zapRequest = author.zaps.filter { it.value == zapEvent }.keys.firstOrNull() + if (zapRequest != null) { + zapsPerUser.getOrPut(author, { mutableMapOf() }) + .put(zapRequest, zapEvent) + } + } } } @@ -121,6 +136,13 @@ open class CardFeedViewModel(val dataSource: FeedFilter) : ViewModel() { } }.flatten() + val userZaps = zapsPerUser.map { + ZapUserSetCard( + it.key, + it.value + ) + } + val textNoteCards = notes.filter { it.event !is ReactionEvent && it.event !is RepostEvent && it.event !is LnZapEvent }.map { if (it.event is PrivateDmEvent) { MessageSetCard(it) @@ -131,7 +153,7 @@ open class CardFeedViewModel(val dataSource: FeedFilter) : ViewModel() { } } - return (multiCards + textNoteCards).sortedBy { it.createdAt() }.reversed() + return (multiCards + textNoteCards + userZaps).sortedBy { it.createdAt() }.reversed() } private fun updateFeed(notes: List) {