mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-29 19:43:23 +02:00
Adds notification for Zaps that don't have a Note attached to them.
This commit is contained in:
@@ -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<Boolean>(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.screen
|
|||||||
|
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
|
import com.vitorpamplona.amethyst.model.User
|
||||||
|
|
||||||
abstract class Card() {
|
abstract class Card() {
|
||||||
abstract fun createdAt(): Long
|
abstract fun createdAt(): Long
|
||||||
@@ -40,6 +41,14 @@ class ZapSetCard(val note: Note, val zapEvents: Map<Note, Note>) : Card() {
|
|||||||
override fun id() = note.idHex + "Z" + createdAt
|
override fun id() = note.idHex + "Z" + createdAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ZapUserSetCard(val user: User, val zapEvents: Map<Note, Note>) : 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<Note>, val likeEvents: List<Note>, val zapEvents: Map<Note, Note>) : Card() {
|
class MultiSetCard(val note: Note, val boostEvents: List<Note>, val likeEvents: List<Note>, val zapEvents: Map<Note, Note>) : Card() {
|
||||||
val createdAt = maxOf(
|
val createdAt = maxOf(
|
||||||
zapEvents.maxOfOrNull { it.value.createdAt() ?: 0 } ?: 0,
|
zapEvents.maxOfOrNull { it.value.createdAt() ?: 0 } ?: 0,
|
||||||
|
@@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.ui.note.MessageSetCompose
|
|||||||
import com.vitorpamplona.amethyst.ui.note.MultiSetCompose
|
import com.vitorpamplona.amethyst.ui.note.MultiSetCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.ZapSetCompose
|
import com.vitorpamplona.amethyst.ui.note.ZapSetCompose
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.ZapUserSetCompose
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@@ -128,6 +129,13 @@ private fun FeedLoaded(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
routeForLastRead = routeForLastRead
|
routeForLastRead = routeForLastRead
|
||||||
)
|
)
|
||||||
|
is ZapUserSetCard -> ZapUserSetCompose(
|
||||||
|
item,
|
||||||
|
isInnerNote = false,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
navController = navController,
|
||||||
|
routeForLastRead = routeForLastRead
|
||||||
|
)
|
||||||
is LikeSetCard -> LikeSetCompose(
|
is LikeSetCard -> LikeSetCompose(
|
||||||
item,
|
item,
|
||||||
isInnerNote = false,
|
isInnerNote = false,
|
||||||
|
@@ -7,6 +7,7 @@ import com.vitorpamplona.amethyst.model.Account
|
|||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.LocalCacheState
|
import com.vitorpamplona.amethyst.model.LocalCacheState
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
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.BadgeAwardEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
|
||||||
@@ -78,7 +79,7 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// val reactionCards = reactionsPerEvent.map { LikeSetCard(it.key, it.value) }
|
// val reactionCards = reactionsPerEvent.map { LikeSetCard(it.key, it.value) }
|
||||||
|
val zapsPerUser = mutableMapOf<User, MutableMap<Note, Note>>()
|
||||||
val zapsPerEvent = mutableMapOf<Note, MutableMap<Note, Note>>()
|
val zapsPerEvent = mutableMapOf<Note, MutableMap<Note, Note>>()
|
||||||
notes
|
notes
|
||||||
.filter { it.event is LnZapEvent }
|
.filter { it.event is LnZapEvent }
|
||||||
@@ -89,6 +90,20 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
|||||||
if (zapRequest != null) {
|
if (zapRequest != null) {
|
||||||
zapsPerEvent.getOrPut(zappedPost, { mutableMapOf() }).put(zapRequest, zapEvent)
|
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<Note>) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}.flatten()
|
}.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 {
|
val textNoteCards = notes.filter { it.event !is ReactionEvent && it.event !is RepostEvent && it.event !is LnZapEvent }.map {
|
||||||
if (it.event is PrivateDmEvent) {
|
if (it.event is PrivateDmEvent) {
|
||||||
MessageSetCard(it)
|
MessageSetCard(it)
|
||||||
@@ -131,7 +153,7 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (multiCards + textNoteCards).sortedBy { it.createdAt() }.reversed()
|
return (multiCards + textNoteCards + userZaps).sortedBy { it.createdAt() }.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFeed(notes: List<Card>) {
|
private fun updateFeed(notes: List<Card>) {
|
||||||
|
Reference in New Issue
Block a user